<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebSocketBinding;
use dom::bindings::codegen::Bindings::WebSocketBinding::WebSocketMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::codegen::InheritTypes::EventCast;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::error::Error::InvalidAccess;
use dom::bindings::error::Error::Syntax;
use dom::bindings::global::{GlobalField, GlobalRef};
use dom::bindings::js::{Temporary, JSRef, Rootable};
use dom::bindings::refcounted::Trusted;
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::reflect_dom_object;
use dom::closeevent::CloseEvent;
use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers};
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use script_task::Runnable;
use script_task::ScriptMsg;
use std::cell::{Cell, RefCell};
use std::borrow::ToOwned;
use util::str::DOMString;

use websocket::Message;
use websocket::ws::sender::Sender as Sender_Object;
use websocket::client::sender::Sender;
use websocket::client::receiver::Receiver;
use websocket::stream::WebSocketStream;
use websocket::client::request::Url;
use websocket::Client;

#[derive(PartialEq, Copy)]
#[jstraceable]
enum WebSocketRequestState {
    Connecting = 0,
    Open = 1,
    Closing = 2,
    Closed = 3,
}

no_jsmanaged_fields!(Sender<WebSocketStream>);
no_jsmanaged_fields!(Receiver<WebSocketStream>);

#[dom_struct]
pub struct WebSocket { //Websocket attributes defined here
    eventtarget: EventTarget,
    url: DOMString,
    global: GlobalField,
    ready_state: Cell<WebSocketRequestState>,
    sender: RefCell<Option<Sender<WebSocketStream>>>,
    receiver: RefCell<Option<Receiver<WebSocketStream>>>,
    failed: Cell<bool>, //Flag to tell if websocket was closed due to failure
    full: Cell<bool>, //Flag to tell if websocket queue is full
    clean_close: Cell<bool>, //Flag to tell if the websocket closed cleanly (not due to full or fail)
    code: Cell<u16>, //Closing code
    reason: DOMRefCell<DOMString>, //Closing reason
    data: DOMRefCell<DOMString>, //Data from send
  sendCloseFrame: Cell<bool>
}

impl WebSocket {
    pub fn new_inherited(global: GlobalRef, url: DOMString) -> WebSocket {
        WebSocket {
            eventtarget: EventTarget::new_inherited(EventTargetTypeId::WebSocket),
            url: url,
            global: GlobalField::from_rooted(&global),
            ready_state: Cell::new(WebSocketRequestState::Connecting),
            failed: Cell::new(false),
            sender: RefCell::new(None),
            receiver: RefCell::new(None),
            full: Cell::new(false),
            clean_close: Cell::new(true),
            code: Cell::new(0),
            reason: DOMRefCell::new("".to_owned()),
            data: DOMRefCell::new("".to_owned()),
            sendCloseFrame: Cell::new(false)
        }

    }

    pub fn new(global: GlobalRef, url: DOMString) -> Temporary<WebSocket> {
        /*TODO: This constructor is only a prototype, it does not accomplish the specs
          defined here:
          http://html.spec.whatwg.org
          All 9 items must be satisfied.
          TODO: This constructor should be responsible for spawning a thread for the
          receive loop after ws_root.r().Open() - See comment
        */
        let ws_root = reflect_dom_object(box WebSocket::new_inherited(global, url),
                          global,
                          WebSocketBinding::Wrap).root();
        let ws_root = ws_root.r();
        let parsed_url = Url::parse(ws_root.url.as_slice()).unwrap(); //Get url
        let request = Client::connect(parsed_url).unwrap(); //Set up the request
        let response = request.send().unwrap(); //Send the request to the server
        response.validate().unwrap(); //Validate the server's response
        ws_root.ready_state.set(WebSocketRequestState::Open); //Set state to open
        //Check to see if ready_state is Closing or Closed and failed = true - means we failed the websocket
        //if so return without setting any states
        let ready_state = ws_root.ready_state.get();
        let failed = ws_root.failed.get();
        if failed && (ready_state == WebSocketRequestState::Closed || ready_state == WebSocketRequestState::Closing) {
            //Do nothing else. Let the close finish.
            return Temporary::from_rooted(ws_root);
        }
        let (temp_sender, temp_receiver) = response.begin().split(); //Set send and receiver in the attributes
        let mut other_sender = ws_root.sender.borrow_mut();
        let mut other_receiver = ws_root.receiver.borrow_mut();
        *other_sender = Some(temp_sender);
        *other_receiver = Some(temp_receiver);

        //Create everything necessary for starting the open asynchronous task, then begin the task.
        let global_root = ws_root.global.root();
        let addr: Trusted<WebSocket> = Trusted::new(global_root.r().get_cx(), ws_root, global_root.r().script_chan().clone());
        let open_task = box WebSocketTaskHandler::new(addr.clone(), WebSocketTask::Open);
        global_root.r().script_chan().send(ScriptMsg::RunnableMsg(open_task)).unwrap();
        //TODO: Spawn thread here for receive loop
        /*TODO: Add receive loop here and make new thread run this
          Receive is an infinite loop "similiar" the one shown here:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: The receive loop however does need to follow the spec. These are outlined here
          under "WebSocket message has been received" items 1-5:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: The receive loop also needs to dispatch an asynchronous event as stated here:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: When the receive loop receives a close message from the server,
          it confirms the websocket is now closed. This requires the close event
          to be fired (dispatch_close fires the close event - see implementation below)
        */
        Temporary::from_rooted(ws_root) //Return the websocket from the original thread that called this constructor
    }

    pub fn Constructor(global: GlobalRef, url: DOMString) -> Fallible<Temporary<WebSocket>> {
        Ok(WebSocket::new(global, url))
    }
}

impl<'a> WebSocketMethods for JSRef<'a, WebSocket> {
    event_handler!(open, GetOnopen, SetOnopen);
    event_handler!(close, GetOnclose, SetOnclose);
    event_handler!(error, GetOnerror, SetOnerror);

    fn Url(self) -> DOMString {
        self.url.clone()
    }

    fn ReadyState(self) -> u16 {
        self.ready_state.get() as u16
    }

    fn Send(self, data: Option<DOMString>)-> Fallible<()>{
        /*TODO: This is not up to spec see http://html.spec.whatwg.org/multipage/comms.html search for "If argument is a string"
          TODO: Need to buffer data
          TODO: bufferedAmount attribute returns the size of the buffer in bytes - this is a required attribute defined in the websocket.webidle file
          TODO: The send function needs to flag when full by using the following
          self.full.set(true). This needs to be done when the buffer is full
        */
        let mut other_sender = self.sender.borrow_mut();
        let my_sender = other_sender.as_mut().unwrap();
        if self.sendCloseFrame.get() { //TODO: Also check if the buffer is full
            self.sendCloseFrame.set(false); //Deflag the close frame request
            let _ = my_sender.send_message(Message::Close(None)); //Send close frame
            return Ok(());
        }
        let _ = my_sender.send_message(Message::Text(data.unwrap()));
        return Ok(())
    }

    /*TODO: 3 other types of send as specified in webidle/WebSocket.webidl
      The specs for each of these functions are described here:
      https://html.spec.whatwg.org/multipage/comms.html
    */
    //void send(Blob data); //Search above reference for "If the argument is a blob"
    //void send(ArrayBuffer data); //Search above reference for "If the argument is an ArrayBuffer"
    //void send(ArrayBufferView data); //Search above reference for "If the argument is an object that matches the ArrayBufferView"

    //Close is up to spec! :)
    fn Close(self, code: Option<u16>, reason: Option<DOMString>) -> Fallible<()>{
        if code.is_some() { //Code defined
            //Check code is NOT 1000 NOR in the range of 3000-4999 (inclusive)
            if  code != Some(1000) && (code < Some(3000) || code > Some(4999))
            {
                return Err(Error::InvalidAccess); //Throw InvalidAccessError and abort
            }
        }
        if reason.is_some() { //reason defined
            let a_reason = reason.as_ref().unwrap();
            if a_reason.as_bytes().len() > 123 //reason cannot be larger than 123 bytes
            {
                return Err(Error::Syntax); //Throw SyntaxError and abort
            }
        }

        match self.ready_state.get() {
            WebSocketRequestState::Closing | WebSocketRequestState::Closed  => {} //Do nothing
            WebSocketRequestState::Connecting => { //Connection is not yet established
                /*By setting the state to closing, the open function
                  will abort connecting the websocket*/
                self.ready_state.set(WebSocketRequestState::Closing);
                self.failed.set(true);
                self.sendCloseFrame.set(true);
                //-----Dispatch send task to send close frame------//
                //TODO: Sending here is just empty string, though no string is really needed. Another send, empty send, could be used.
                let _ = self.Send(None);
                //Note: After sending the close message, the receive loop confirms a close message from the server and must fire a close event
            }
            WebSocketRequestState::Open => {
                //Closing handshake not started - still in open
                //Start the closing by setting the code and reason if they exist
                if code.is_some() {
                    self.code.set(code.unwrap());
                }
                if reason.is_some(){
                    *self.reason.borrow_mut() = reason.unwrap();
                }
                self.ready_state.set(WebSocketRequestState::Closing); //Set state to closing
                self.sendCloseFrame.set(true); //Set close frame flag
                //Dispatch send task to send close frame
                let _ = self.Send(None);
                //Note: After sending the close message, the receive loop confirms a close message from the server and must fire a close event
            }
            //_ => { self.ready_state.set(WebSocketRequestState::Closing); } //Unreachable - Uncomment if you add more states to WebSocketRequestState
        }
        Ok(()) //Return Ok
    }
}

pub enum WebSocketTask {
    Open,
    Close,
}

pub struct WebSocketTaskHandler {
    addr: Trusted<WebSocket>,
    task: WebSocketTask,
}

impl WebSocketTaskHandler {
    pub fn new(addr: Trusted<WebSocket>, task: WebSocketTask) -> WebSocketTaskHandler {
        WebSocketTaskHandler {
            addr: addr,
            task: task,
        }
    }

    fn dispatch_open(&self) {
        /*TODO: Items 1, 3, 4, & 5 under "WebSocket connection is established" as specified here:
          https://html.spec.whatwg.org
        */
        let ws = self.addr.to_temporary().root(); //Get root
        let ws = ws.r(); //Get websocket reference
        let global = ws.global.root();
        let event = Event::new(global.r(),
            "open".to_owned(),
            EventBubbles::DoesNotBubble,
            EventCancelable::Cancelable).root();
        let target: JSRef<EventTarget> = EventTargetCast::from_ref(ws);
        event.r().fire(target);
    }

    fn dispatch_close(&self) {
        let ws = self.addr.to_temporary().root();
        let global = ws.r().global.root();
        ws.r().ready_state.set(WebSocketRequestState::Closed); //Set to closed state
        //If failed or full, fire error event
        if ws.r().failed.get()||ws.r().full.get() {
            //Unset failed flag so we don't cause false positives
            ws.r().failed.set(false);
            //Unset full flag so we don't cause false positives
            ws.r().full.set(false);
            //A Bad close
            ws.r().clean_close.set(false);
            let event = Event::new(global.r(),
                "error".to_owned(),
                EventBubbles::DoesNotBubble,
                EventCancelable::Cancelable).root();
            let target: JSRef<EventTarget> = EventTargetCast::from_ref(ws.r());
            event.r().fire(target);
        }
        let ws = ws.r();
        let rsn = ws.reason.borrow();
        let rsn_clone = rsn.clone();
        /*In addition, we also have to fire a close even if error event fired
         https://html.spec.whatwg.org
        */
        let close_event = CloseEvent::new(global.r(),
                "close".to_owned(),
                EventBubbles::DoesNotBubble,
            EventCancelable::Cancelable,
            ws.clean_close.get(),
            ws.code.get(),
            rsn_clone
            ).root();
        let target: JSRef<EventTarget> = EventTargetCast::from_ref(ws);
        let event: JSRef<Event> = EventCast::from_ref(close_event.r());
        event.set_trusted(true);
        event.fire(target);
    }
}

impl Runnable for WebSocketTaskHandler {
    fn handler(self: Box<WebSocketTaskHandler>) {
        match self.task {
            WebSocketTask::Open => {
                self.dispatch_open();
            }
            WebSocketTask::Close => {
                self.dispatch_close();
            }
        }
    }
}

<commit_msg>Removed some comments and fixed spacing.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebSocketBinding;
use dom::bindings::codegen::Bindings::WebSocketBinding::WebSocketMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::codegen::InheritTypes::EventCast;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::error::Error::InvalidAccess;
use dom::bindings::error::Error::Syntax;
use dom::bindings::global::{GlobalField, GlobalRef};
use dom::bindings::js::{Temporary, JSRef, Rootable};
use dom::bindings::refcounted::Trusted;
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::reflect_dom_object;
use dom::closeevent::CloseEvent;
use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers};
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use script_task::Runnable;
use script_task::ScriptMsg;
use std::cell::{Cell, RefCell};
use std::borrow::ToOwned;
use util::str::DOMString;

use websocket::Message;
use websocket::ws::sender::Sender as Sender_Object;
use websocket::client::sender::Sender;
use websocket::client::receiver::Receiver;
use websocket::stream::WebSocketStream;
use websocket::client::request::Url;
use websocket::Client;

#[derive(PartialEq, Copy)]
#[jstraceable]
enum WebSocketRequestState {
    Connecting = 0,
    Open = 1,
    Closing = 2,
    Closed = 3,
}

no_jsmanaged_fields!(Sender<WebSocketStream>);
no_jsmanaged_fields!(Receiver<WebSocketStream>);

#[dom_struct]
pub struct WebSocket {
    eventtarget: EventTarget,
    url: DOMString,
    global: GlobalField,
    ready_state: Cell<WebSocketRequestState>,
    sender: RefCell<Option<Sender<WebSocketStream>>>,
    receiver: RefCell<Option<Receiver<WebSocketStream>>>,
    failed: Cell<bool>, //Flag to tell if websocket was closed due to failure
    full: Cell<bool>, //Flag to tell if websocket queue is full
    clean_close: Cell<bool>, //Flag to tell if the websocket closed cleanly (not due to full or fail)
    code: Cell<u16>, //Closing code
    reason: DOMRefCell<DOMString>, //Closing reason
    data: DOMRefCell<DOMString>, //Data from send - TODO: Remove after buffer is added.
    sendCloseFrame: Cell<bool>
}

impl WebSocket {
    pub fn new_inherited(global: GlobalRef, url: DOMString) -> WebSocket {
        WebSocket {
            eventtarget: EventTarget::new_inherited(EventTargetTypeId::WebSocket),
            url: url,
            global: GlobalField::from_rooted(&global),
            ready_state: Cell::new(WebSocketRequestState::Connecting),
            failed: Cell::new(false),
            sender: RefCell::new(None),
            receiver: RefCell::new(None),
            full: Cell::new(false),
            clean_close: Cell::new(true),
            code: Cell::new(0),
            reason: DOMRefCell::new("".to_owned()),
            data: DOMRefCell::new("".to_owned()),
            sendCloseFrame: Cell::new(false)
        }

    }

    pub fn new(global: GlobalRef, url: DOMString) -> Temporary<WebSocket> {
        /*TODO: This constructor is only a prototype, it does not accomplish the specs
          defined here:
          http://html.spec.whatwg.org
          All 9 items must be satisfied.
          TODO: This constructor should be responsible for spawning a thread for the
          receive loop after ws_root.r().Open() - See comment
        */
        let ws_root = reflect_dom_object(box WebSocket::new_inherited(global, url),
                          global,
                          WebSocketBinding::Wrap).root();
        let ws_root = ws_root.r();
        let parsed_url = Url::parse(ws_root.url.as_slice()).unwrap(); //Get url
        let request = Client::connect(parsed_url).unwrap(); //Set up the request
        let response = request.send().unwrap(); //Send the request to the server
        response.validate().unwrap(); //Validate the server's response
        ws_root.ready_state.set(WebSocketRequestState::Open); //Set state to open
        //Check to see if ready_state is Closing or Closed and failed = true - means we failed the websocket
        //if so return without setting any states
        let ready_state = ws_root.ready_state.get();
        let failed = ws_root.failed.get();
        if failed && (ready_state == WebSocketRequestState::Closed || ready_state == WebSocketRequestState::Closing) {
            //Do nothing else. Let the close finish.
            return Temporary::from_rooted(ws_root);
        }
        let (temp_sender, temp_receiver) = response.begin().split(); //Set send and receiver in the attributes
        let mut other_sender = ws_root.sender.borrow_mut();
        let mut other_receiver = ws_root.receiver.borrow_mut();
        *other_sender = Some(temp_sender);
        *other_receiver = Some(temp_receiver);

        //Create everything necessary for starting the open asynchronous task, then begin the task.
        let global_root = ws_root.global.root();
        let addr: Trusted<WebSocket> = Trusted::new(global_root.r().get_cx(), ws_root, global_root.r().script_chan().clone());
        let open_task = box WebSocketTaskHandler::new(addr.clone(), WebSocketTask::Open);
        global_root.r().script_chan().send(ScriptMsg::RunnableMsg(open_task)).unwrap();
        //TODO: Spawn thread here for receive loop
        /*TODO: Add receive loop here and make new thread run this
          Receive is an infinite loop "similiar" the one shown here:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: The receive loop however does need to follow the spec. These are outlined here
          under "WebSocket message has been received" items 1-5:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: The receive loop also needs to dispatch an asynchronous event as stated here:
          https://github.com/cyderize/rust-websocket/blob/master/examples/client.rs#L64
          TODO: When the receive loop receives a close message from the server,
          it confirms the websocket is now closed. This requires the close event
          to be fired (dispatch_close fires the close event - see implementation below)
        */
        Temporary::from_rooted(ws_root)
    }

    pub fn Constructor(global: GlobalRef, url: DOMString) -> Fallible<Temporary<WebSocket>> {
        Ok(WebSocket::new(global, url))
    }
}

impl<'a> WebSocketMethods for JSRef<'a, WebSocket> {
    event_handler!(open, GetOnopen, SetOnopen);
    event_handler!(close, GetOnclose, SetOnclose);
    event_handler!(error, GetOnerror, SetOnerror);

    fn Url(self) -> DOMString {
        self.url.clone()
    }

    fn ReadyState(self) -> u16 {
        self.ready_state.get() as u16
    }

    fn Send(self, data: Option<DOMString>)-> Fallible<()>{
        /*TODO: This is not up to spec see http://html.spec.whatwg.org/multipage/comms.html search for "If argument is a string"
          TODO: Need to buffer data
          TODO: bufferedAmount attribute returns the size of the buffer in bytes - this is a required attribute defined in the websocket.webidle file
          TODO: The send function needs to flag when full by using the following
          self.full.set(true). This needs to be done when the buffer is full
        */
        let mut other_sender = self.sender.borrow_mut();
        let my_sender = other_sender.as_mut().unwrap();
        if self.sendCloseFrame.get() { //TODO: Also check if the buffer is full
            self.sendCloseFrame.set(false); //Deflag the close frame request
            let _ = my_sender.send_message(Message::Close(None)); //Send close frame
            return Ok(());
        }
        let _ = my_sender.send_message(Message::Text(data.unwrap()));
        return Ok(())
    }

    /*TODO: 3 other types of send as specified in webidle/WebSocket.webidl
      The specs for each of these functions are described here:
      https://html.spec.whatwg.org/multipage/comms.html
    */
    //void send(Blob data); //Search above reference for "If the argument is a blob"
    //void send(ArrayBuffer data); //Search above reference for "If the argument is an ArrayBuffer"
    //void send(ArrayBufferView data); //Search above reference for "If the argument is an object that matches the ArrayBufferView"

    //Close is up to spec! :)
    fn Close(self, code: Option<u16>, reason: Option<DOMString>) -> Fallible<()>{
        if code.is_some() { //Code defined
            //Check code is NOT 1000 NOR in the range of 3000-4999 (inclusive)
            if  code != Some(1000) && (code < Some(3000) || code > Some(4999))
            {
                return Err(Error::InvalidAccess); //Throw InvalidAccessError and abort
            }
        }
        if reason.is_some() { //reason defined
            let a_reason = reason.as_ref().unwrap();
            if a_reason.as_bytes().len() > 123 //reason cannot be larger than 123 bytes
            {
                return Err(Error::Syntax); //Throw SyntaxError and abort
            }
        }

        match self.ready_state.get() {
            WebSocketRequestState::Closing | WebSocketRequestState::Closed  => {} //Do nothing
            WebSocketRequestState::Connecting => { //Connection is not yet established
                /*By setting the state to closing, the open function
                  will abort connecting the websocket*/
                self.ready_state.set(WebSocketRequestState::Closing);
                self.failed.set(true);
                self.sendCloseFrame.set(true);
                //-----Dispatch send task to send close frame------//
                //TODO: Sending here is just empty string, though no string is really needed. Another send, empty send, could be used.
                let _ = self.Send(None);
                //Note: After sending the close message, the receive loop confirms a close message from the server and must fire a close event
            }
            WebSocketRequestState::Open => {
                //Closing handshake not started - still in open
                //Start the closing by setting the code and reason if they exist
                if code.is_some() {
                    self.code.set(code.unwrap());
                }
                if reason.is_some(){
                    *self.reason.borrow_mut() = reason.unwrap();
                }
                self.ready_state.set(WebSocketRequestState::Closing); //Set state to closing
                self.sendCloseFrame.set(true); //Set close frame flag
                //Dispatch send task to send close frame
                let _ = self.Send(None);
                //Note: After sending the close message, the receive loop confirms a close message from the server and must fire a close event
            }
            //_ => { self.ready_state.set(WebSocketRequestState::Closing); } //Unreachable - Uncomment if you add more states to WebSocketRequestState
        }
        Ok(()) //Return Ok
    }
}

pub enum WebSocketTask {
    Open,
    Close,
}

pub struct WebSocketTaskHandler {
    addr: Trusted<WebSocket>,
    task: WebSocketTask,
}

impl WebSocketTaskHandler {
    pub fn new(addr: Trusted<WebSocket>, task: WebSocketTask) -> WebSocketTaskHandler {
        WebSocketTaskHandler {
            addr: addr,
            task: task,
        }
    }

    fn dispatch_open(&self) {
        /*TODO: Items 1, 3, 4, & 5 under "WebSocket connection is established" as specified here:
          https://html.spec.whatwg.org
        */
        let ws = self.addr.to_temporary().root(); //Get root
        let ws = ws.r(); //Get websocket reference
        let global = ws.global.root();
        let event = Event::new(global.r(),
            "open".to_owned(),
            EventBubbles::DoesNotBubble,
            EventCancelable::Cancelable).root();
        let target: JSRef<EventTarget> = EventTargetCast::from_ref(ws);
        event.r().fire(target);
    }

    fn dispatch_close(&self) {
        let ws = self.addr.to_temporary().root();
        let global = ws.r().global.root();
        ws.r().ready_state.set(WebSocketRequestState::Closed); //Set to closed state
        //If failed or full, fire error event
        if ws.r().failed.get()||ws.r().full.get() {
            //Unset failed flag so we don't cause false positives
            ws.r().failed.set(false);
            //Unset full flag so we don't cause false positives
            ws.r().full.set(false);
            //A Bad close
            ws.r().clean_close.set(false);
            let event = Event::new(global.r(),
                "error".to_owned(),
                EventBubbles::DoesNotBubble,
                EventCancelable::Cancelable).root();
            let target: JSRef<EventTarget> = EventTargetCast::from_ref(ws.r());
            event.r().fire(target);
        }
        let ws = ws.r();
        let rsn = ws.reason.borrow();
        let rsn_clone = rsn.clone();
        /*In addition, we also have to fire a close even if error event fired
         https://html.spec.whatwg.org
        */
        let close_event = CloseEvent::new(global.r(),
                "close".to_owned(),
                EventBubbles::DoesNotBubble,
            EventCancelable::Cancelable,
            ws.clean_close.get(),
            ws.code.get(),
            rsn_clone
            ).root();
        let target: JSRef<EventTarget> = EventTargetCast::from_ref(ws);
        let event: JSRef<Event> = EventCast::from_ref(close_event.r());
        event.set_trusted(true);
        event.fire(target);
    }
}

impl Runnable for WebSocketTaskHandler {
    fn handler(self: Box<WebSocketTaskHandler>) {
        match self.task {
            WebSocketTask::Open => {
                self.dispatch_open();
            }
            WebSocketTask::Close => {
                self.dispatch_close();
            }
        }
    }
}

<|endoftext|>


<commit_before>// Copyright:: Copyright (c) 2015-2016 The Habitat Maintainers
//
// The terms of the Evaluation Agreement (Habitat) between Chef Software Inc.
// and the party accessing this file ("Licensee") apply to Licensee's use of
// the Software until such time that the Software is made available under an
// open source license such as the Apache 2.0 License.

//! The service topologies.
//!
//! A service topology is a [state machine](../state_machine) that wraps the lifecycle events of a service around the
//! process supervisor and package manager. It is responsible for:
//!
//! 1. Processing the main event loop
//! 1. Registering callbacks with the [discovery](../discovery) system
//!
//! Typically, topologies are created from the [start](../command/start) command.

pub mod standalone;
pub mod leader;
pub mod initializer;

use std::mem;
use std::net::SocketAddrV4;
use std::ops::DerefMut;
use std::str::FromStr;
use std::sync::{Arc, RwLock};
use std::sync::mpsc::TryRecvError;
use std::thread;
use std::time::Duration;

use wonder;

use state_machine::StateMachine;
use census::{self, CensusList};
use common::gossip_file::GossipFileList;
use package::{self, Package, PackageUpdaterActor};
use util::signals::SignalNotifier;
use error::{Result, SupError};
use config::Config;
use service_config::ServiceConfig;
use sidecar;
use supervisor::Supervisor;
use gossip;
use gossip::rumor::{Rumor, RumorList};
use gossip::member::MemberList;
use election::ElectionList;
use time::SteadyTime;
use util::signals;

static LOGKEY: &'static str = "TP";
static MINIMUM_LOOP_TIME_MS: i64 = 200;

#[derive(PartialEq, Eq, Debug, RustcEncodable)]
pub enum Topology {
    Standalone,
    Leader,
    Initializer,
}

impl Default for Topology {
    fn default() -> Topology {
        Topology::Standalone
    }
}

/// Viable states for the topologies. Not every topology will implement every state.
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
pub enum State {
    Init,
    MinimumQuorum,
    WaitingForQuorum,
    RestoreDataset,
    DetermineViability,
    BecomeLeader,
    BecomeFollower,
    CheckForElection,
    Election,
    StartElection,
    InElection,
    Leader,
    Follower,
    Configure,
    Initializing,
    Starting,
    Running,
}

/// The topology `Worker` is where everything our state machine needs between states lives.
pub struct Worker<'a> {
    /// The package we are supervising
    pub package: Arc<RwLock<Package>>,
    /// Name of the package being supervised
    pub package_name: String,
    /// A pointer to our current Config
    pub config: &'a Config,
    /// The topology we are running
    pub topology: String,
    /// Our Service Configuration; manages changes to our configuration,
    pub service_config: Arc<RwLock<ServiceConfig>>,
    /// The Gossip Server; listens for inbound gossip traffic
    pub gossip_server: gossip::server::Server,
    pub census_list: Arc<RwLock<CensusList>>,
    pub rumor_list: Arc<RwLock<RumorList>>,
    pub election_list: Arc<RwLock<ElectionList>>,
    pub member_list: Arc<RwLock<MemberList>>,
    pub gossip_file_list: Arc<RwLock<GossipFileList>>,
    /// Our Sidecar Actor; exposes a restful HTTP interface to the outside world
    pub sidecar_actor: sidecar::SidecarActor,
    /// Watches a package Depot for updates and signals the main thread when an update is available. Optionally
    /// started if a value is passed for the url option on startup.
    pub pkg_updater: Option<PackageUpdaterActor>,
    /// The service supervisor
    pub supervisor: Arc<RwLock<Supervisor>>,
    pub return_state: Option<State>,
}

impl<'a> Worker<'a> {
    /// Create a new worker
    ///
    /// Automatically sets the backend to Etcd.
    pub fn new(package: Package, topology: String, config: &'a Config) -> Result<Worker<'a>> {
        let mut pkg_updater = None;
        let package_name = package.name.clone();
        let package_exposes = package.exposes().clone();
        let package_port = package_exposes.first().map(|e| e.clone());
        let package_ident = package.ident().clone();
        let pkg_lock = Arc::new(RwLock::new(package));
        let pkg_lock_1 = pkg_lock.clone();

        if let Some(ref url) = *config.url() {
            let pkg_lock_2 = pkg_lock.clone();
            pkg_updater = Some(package::PackageUpdater::start(url, pkg_lock_2));
        }

        let gossip_server = gossip::server::Server::new(String::from(config.gossip_listen_ip()),
                                                        config.gossip_listen_port(),
                                                        config.gossip_permanent(),
                                                        config.ring().clone(),
                                                        package_name.clone(),
                                                        config.group().to_string(),
                                                        config.organization().clone(),
                                                        Some(package_exposes),
                                                        package_port);

        try!(gossip_server.start_inbound());
        try!(gossip_server.initial_peers(config.gossip_peer()));
        gossip_server.start_outbound();
        gossip_server.start_failure_detector();
        census::start_health_adjuster(gossip_server.census_list.clone(),
                                      gossip_server.member_list.clone());

        // Setup the Service Configuration
        let service_config = {
            let cl = gossip_server.census_list.read().unwrap();
            let pkg = pkg_lock.read().unwrap();
            let sc = try!(ServiceConfig::new(&config, &pkg, &cl, config.bind()));
            sc
        };
        let service_config_lock = Arc::new(RwLock::new(service_config));
        let service_config_lock_1 = service_config_lock.clone();

        let supervisor = Arc::new(RwLock::new(Supervisor::new(package_ident)));

        let sidecar_ml = gossip_server.member_list.clone();
        let sidecar_rl = gossip_server.rumor_list.clone();
        let sidecar_cl = gossip_server.census_list.clone();
        let sidecar_gfl = gossip_server.gossip_file_list.clone();
        let sidecar_detector = gossip_server.detector.clone();
        let sidecar_el = gossip_server.election_list.clone();
        let sidecar_sup = supervisor.clone();
        let sidecar_listen = try!(SocketAddrV4::from_str(&format!("{}:{}",
                                                                  &config.http_listen_ip(),
                                                                  config.http_listen_port())));
        Ok(Worker {
            package: pkg_lock,
            package_name: package_name,
            topology: topology,
            config: config,
            census_list: gossip_server.census_list.clone(),
            rumor_list: gossip_server.rumor_list.clone(),
            election_list: gossip_server.election_list.clone(),
            gossip_file_list: gossip_server.gossip_file_list.clone(),
            member_list: gossip_server.member_list.clone(),
            gossip_server: gossip_server,
            service_config: service_config_lock,
            sidecar_actor: sidecar::Sidecar::start(sidecar_listen,
                                                   pkg_lock_1,
                                                   service_config_lock_1,
                                                   sidecar_ml,
                                                   sidecar_rl,
                                                   sidecar_cl,
                                                   sidecar_detector,
                                                   sidecar_el,
                                                   sidecar_sup,
                                                   sidecar_gfl),
            supervisor: supervisor,
            pkg_updater: pkg_updater,
            return_state: None,
        })
    }

    /// update a package, but does NOT restart the service
    pub fn update_package(&self, updated: Package) -> Result<()> {
        let service_config = self.service_config.read().unwrap();
        {
            let mut package = self.package.write().unwrap();
            mem::replace(package.deref_mut(), updated);
        }
        let package = self.package.read().unwrap();
        try!(package.copy_run(&service_config));
        Ok(())
    }
}

/// The main loop of a topology.
///
/// 1. Loops forever
/// 1. Checks if we have caught a signal; if so, acts on the signal. (May exit entirely)
/// 1. Checks the current `state` of our [StateMachine](../state_machine)
/// 1. If it is running, we run a non-blocking `waitpid`, and inspect why the child died;
///    depending on the circumstances, we may exit with an error here
/// 1. Process any discovery events
/// 1. Trigger the next iteration of the state machine
///
/// # Failures
///
/// * We fail to send a signal to the supervisor
/// * We fail to join the supervisor thread
/// * The supervisor dies unexpectedly
/// * The discovery subsystem returns an error
/// * The topology state machine returns an error
fn run_internal<'a>(sm: &mut StateMachine<State, Worker<'a>, SupError>,
                    worker: &mut Worker<'a>)
                    -> Result<()> {
    {
        let package = worker.package.read().unwrap();
        let service_config = worker.service_config.read().unwrap();
        try!(package.create_svc_path());
        try!(package.copy_run(&service_config));
    }
    let handler = wonder::actor::Builder::new(SignalNotifier)
        .name("signal-handler".to_string())
        .start(())
        .unwrap();
    loop {
        let start_time = SteadyTime::now();
        match handler.receiver.try_recv() {
            Ok(wonder::actor::Message::Cast(signals::Message::Signal(sig))) => {
                debug!("SIG = {:?}", sig);
                match sig {
                    signals::Signal::SIGINT | signals::Signal::SIGTERM => {
                        let mut supervisor = worker.supervisor.write().unwrap();
                        try!(supervisor.down());
                        break;
                    }
                    _ => {
                        outputln!("Forwarding {:?} on to the supervised process", sig);
                        let supervisor = worker.supervisor.write().unwrap();
                        try!(supervisor.send_unix_signal(sig.clone()));
                    }
                };
            }
            Ok(_) => {}
            Err(TryRecvError::Empty) => {}
            Err(TryRecvError::Disconnected) => {
                panic!("signal handler crashed!");
            }
        }

        {
            let mut supervisor = worker.supervisor.write().unwrap();
            try!(supervisor.check_process());
        }

        let mut restart_process = false;

        // This section, and the following really need to be refactored:
        //
        // 1. We check to see if we are in an event a bunch of times
        // 2. We potentially restart 3 different times - twice for reconfigure, and once for
        //    file_udpated. That's pretty yucky.
        //
        // Love, Adam
        {
            // Manage the census
            let (write_census, in_event, write_rumor, me_clone) = {
                let cl = worker.census_list.read().unwrap();
                let census = cl.local_census();
                (cl.needs_write(), census.in_event, census.me().needs_write(), census.me().clone())
            };

            if write_census {
                if !in_event {
                    let mut service_config = worker.service_config.write().unwrap();
                    let cl = worker.census_list.read().unwrap();
                    service_config.svc(&cl);
                    service_config.bind(worker.config.bind(), &cl);
                }
                if write_rumor {
                    outputln!("Writing our census rumor: {:#?}", me_clone);
                    let mut rl = worker.rumor_list.write().unwrap();
                    rl.add_rumor(Rumor::census_entry(me_clone));
                }
                let mut cl = worker.census_list.write().unwrap();
                cl.written();
            }

            // Don't bother trying to reconfigure if we are in an event - just wait till
            // everything settles down.
            {
                let census_list = worker.census_list.read().unwrap();
                let census = census_list.local_census();
                if !census.in_event {
                    let mut service_config = worker.service_config.write().unwrap();
                    if service_config.needs_write {
                        let package = worker.package.read().unwrap();
                        // Write the configuration, and restart if needed
                        if try!(service_config.write(&package)) {
                            try!(package.copy_run(&service_config));
                            try!(package.reconfigure(&service_config));
                            outputln!("Restarting because the service config was updated via the \
                                       census");
                            restart_process = true;
                        }
                    }
                }
            }
        }

        {
            let in_event = {
                let census_list = worker.census_list.read().unwrap();
                let census = census_list.local_census();
                census.in_event
            };
            let (needs_file_updated, needs_reconfigure) = if in_event {
                (false, false)
            } else {
                let mut gossip_file_list = worker.gossip_file_list.write().unwrap();
                let needs_write = {
                    gossip_file_list.needs_write()
                };
                if needs_write {
                    try!(gossip_file_list.write())
                } else {
                    (false, false)
                }
            };
            if needs_file_updated {
                let service_config = worker.service_config.read().unwrap();
                let package = worker.package.read().unwrap();
                let existed = try!(package.file_updated(&service_config));
                if !existed {
                    restart_process = true;
                }
            }
            if needs_reconfigure {
                let mut service_config = worker.service_config.write().unwrap();
                let package = worker.package.read().unwrap();
                service_config.cfg(&package);
                if try!(service_config.write(&package)) {
                    try!(package.copy_run(&service_config));
                    let existed = try!(package.reconfigure(&service_config));
                    if !existed {
                        restart_process = true;
                    }
                }
            }
        }

        if let Some(ref updater) = worker.pkg_updater {
            match updater.receiver.try_recv() {
                Ok(wonder::actor::Message::Cast(package::UpdaterMessage::Update(package))) => {
                    debug!("Main loop received package update notification: {:?}",
                           &package);
                    try!(worker.update_package(package));
                    try!(package::PackageUpdater::run(&updater));
                    // force the package to restart
                    outputln!("Restarting because the package was updated");
                    restart_process = true;
                }
                Ok(_) => {}
                Err(TryRecvError::Empty) => {}
                Err(TryRecvError::Disconnected) => {
                    panic!("package updater crashed!");
                }
            }
        }

        {
            let mut supervisor = worker.supervisor.write().unwrap();
            // If our target is that the process is up
            if supervisor.is_up() {
                // And no process is running
                if supervisor.pid.is_none() {
                    // Start a new one
                    try!(supervisor.start());
                } else {
                    // If we were supposed to restart
                    if restart_process {
                        // And we have ever started before...
                        if supervisor.has_started {
                            // Restart
                            try!(supervisor.restart());
                        }
                    }
                }
            }
        }

        // Next state!
        try!(sm.next(worker));

        // Slow down our loop
        let elapsed_time = SteadyTime::now() - start_time;
        let elapsed_millis = elapsed_time.num_milliseconds();

        if elapsed_millis < MINIMUM_LOOP_TIME_MS {
            thread::sleep(Duration::from_millis((MINIMUM_LOOP_TIME_MS - elapsed_millis) as u64));
        }
    }
    Ok(())
}
<commit_msg>Fixes auto-update always updating<commit_after>// Copyright:: Copyright (c) 2015-2016 The Habitat Maintainers
//
// The terms of the Evaluation Agreement (Habitat) between Chef Software Inc.
// and the party accessing this file ("Licensee") apply to Licensee's use of
// the Software until such time that the Software is made available under an
// open source license such as the Apache 2.0 License.

//! The service topologies.
//!
//! A service topology is a [state machine](../state_machine) that wraps the lifecycle events of a service around the
//! process supervisor and package manager. It is responsible for:
//!
//! 1. Processing the main event loop
//! 1. Registering callbacks with the [discovery](../discovery) system
//!
//! Typically, topologies are created from the [start](../command/start) command.

pub mod standalone;
pub mod leader;
pub mod initializer;

use std::mem;
use std::net::SocketAddrV4;
use std::ops::DerefMut;
use std::str::FromStr;
use std::sync::{Arc, RwLock};
use std::sync::mpsc::TryRecvError;
use std::thread;
use std::time::Duration;

use wonder;

use state_machine::StateMachine;
use census::{self, CensusList};
use common::gossip_file::GossipFileList;
use package::{self, Package, PackageUpdaterActor};
use util::signals::SignalNotifier;
use error::{Result, SupError};
use config::Config;
use service_config::ServiceConfig;
use sidecar;
use supervisor::Supervisor;
use gossip;
use gossip::rumor::{Rumor, RumorList};
use gossip::member::MemberList;
use election::ElectionList;
use time::SteadyTime;
use util::signals;
use config::UpdateStrategy;

static LOGKEY: &'static str = "TP";
static MINIMUM_LOOP_TIME_MS: i64 = 200;

#[derive(PartialEq, Eq, Debug, RustcEncodable)]
pub enum Topology {
    Standalone,
    Leader,
    Initializer,
}

impl Default for Topology {
    fn default() -> Topology {
        Topology::Standalone
    }
}

/// Viable states for the topologies. Not every topology will implement every state.
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
pub enum State {
    Init,
    MinimumQuorum,
    WaitingForQuorum,
    RestoreDataset,
    DetermineViability,
    BecomeLeader,
    BecomeFollower,
    CheckForElection,
    Election,
    StartElection,
    InElection,
    Leader,
    Follower,
    Configure,
    Initializing,
    Starting,
    Running,
}

/// The topology `Worker` is where everything our state machine needs between states lives.
pub struct Worker<'a> {
    /// The package we are supervising
    pub package: Arc<RwLock<Package>>,
    /// Name of the package being supervised
    pub package_name: String,
    /// A pointer to our current Config
    pub config: &'a Config,
    /// The topology we are running
    pub topology: String,
    /// Our Service Configuration; manages changes to our configuration,
    pub service_config: Arc<RwLock<ServiceConfig>>,
    /// The Gossip Server; listens for inbound gossip traffic
    pub gossip_server: gossip::server::Server,
    pub census_list: Arc<RwLock<CensusList>>,
    pub rumor_list: Arc<RwLock<RumorList>>,
    pub election_list: Arc<RwLock<ElectionList>>,
    pub member_list: Arc<RwLock<MemberList>>,
    pub gossip_file_list: Arc<RwLock<GossipFileList>>,
    /// Our Sidecar Actor; exposes a restful HTTP interface to the outside world
    pub sidecar_actor: sidecar::SidecarActor,
    /// Watches a package Depot for updates and signals the main thread when an update is available. Optionally
    /// started if a value is passed for the url option on startup.
    pub pkg_updater: Option<PackageUpdaterActor>,
    /// The service supervisor
    pub supervisor: Arc<RwLock<Supervisor>>,
    pub return_state: Option<State>,
}

impl<'a> Worker<'a> {
    /// Create a new worker
    ///
    /// Automatically sets the backend to Etcd.
    pub fn new(package: Package, topology: String, config: &'a Config) -> Result<Worker<'a>> {
        let mut pkg_updater = None;
        let package_name = package.name.clone();
        let package_exposes = package.exposes().clone();
        let package_port = package_exposes.first().map(|e| e.clone());
        let package_ident = package.ident().clone();
        let pkg_lock = Arc::new(RwLock::new(package));
        let pkg_lock_1 = pkg_lock.clone();

        match config.update_strategy() {
            UpdateStrategy::None => {},
            _ => {
                let pkg_lock_2 = pkg_lock.clone();
                if let &Some(ref url) = config.url() {
                    pkg_updater = Some(package::PackageUpdater::start(url, pkg_lock_2));
                }
            }
        }

        let gossip_server = gossip::server::Server::new(String::from(config.gossip_listen_ip()),
                                                        config.gossip_listen_port(),
                                                        config.gossip_permanent(),
                                                        config.ring().clone(),
                                                        package_name.clone(),
                                                        config.group().to_string(),
                                                        config.organization().clone(),
                                                        Some(package_exposes),
                                                        package_port);

        try!(gossip_server.start_inbound());
        try!(gossip_server.initial_peers(config.gossip_peer()));
        gossip_server.start_outbound();
        gossip_server.start_failure_detector();
        census::start_health_adjuster(gossip_server.census_list.clone(),
                                      gossip_server.member_list.clone());

        // Setup the Service Configuration
        let service_config = {
            let cl = gossip_server.census_list.read().unwrap();
            let pkg = pkg_lock.read().unwrap();
            let sc = try!(ServiceConfig::new(&config, &pkg, &cl, config.bind()));
            sc
        };
        let service_config_lock = Arc::new(RwLock::new(service_config));
        let service_config_lock_1 = service_config_lock.clone();

        let supervisor = Arc::new(RwLock::new(Supervisor::new(package_ident)));

        let sidecar_ml = gossip_server.member_list.clone();
        let sidecar_rl = gossip_server.rumor_list.clone();
        let sidecar_cl = gossip_server.census_list.clone();
        let sidecar_gfl = gossip_server.gossip_file_list.clone();
        let sidecar_detector = gossip_server.detector.clone();
        let sidecar_el = gossip_server.election_list.clone();
        let sidecar_sup = supervisor.clone();
        let sidecar_listen = try!(SocketAddrV4::from_str(&format!("{}:{}",
                                                                  &config.http_listen_ip(),
                                                                  config.http_listen_port())));
        Ok(Worker {
            package: pkg_lock,
            package_name: package_name,
            topology: topology,
            config: config,
            census_list: gossip_server.census_list.clone(),
            rumor_list: gossip_server.rumor_list.clone(),
            election_list: gossip_server.election_list.clone(),
            gossip_file_list: gossip_server.gossip_file_list.clone(),
            member_list: gossip_server.member_list.clone(),
            gossip_server: gossip_server,
            service_config: service_config_lock,
            sidecar_actor: sidecar::Sidecar::start(sidecar_listen,
                                                   pkg_lock_1,
                                                   service_config_lock_1,
                                                   sidecar_ml,
                                                   sidecar_rl,
                                                   sidecar_cl,
                                                   sidecar_detector,
                                                   sidecar_el,
                                                   sidecar_sup,
                                                   sidecar_gfl),
            supervisor: supervisor,
            pkg_updater: pkg_updater,
            return_state: None,
        })
    }

    /// update a package, but does NOT restart the service
    pub fn update_package(&self, updated: Package) -> Result<()> {
        let service_config = self.service_config.read().unwrap();
        {
            let mut package = self.package.write().unwrap();
            mem::replace(package.deref_mut(), updated);
        }
        let package = self.package.read().unwrap();
        try!(package.copy_run(&service_config));
        Ok(())
    }
}

/// The main loop of a topology.
///
/// 1. Loops forever
/// 1. Checks if we have caught a signal; if so, acts on the signal. (May exit entirely)
/// 1. Checks the current `state` of our [StateMachine](../state_machine)
/// 1. If it is running, we run a non-blocking `waitpid`, and inspect why the child died;
///    depending on the circumstances, we may exit with an error here
/// 1. Process any discovery events
/// 1. Trigger the next iteration of the state machine
///
/// # Failures
///
/// * We fail to send a signal to the supervisor
/// * We fail to join the supervisor thread
/// * The supervisor dies unexpectedly
/// * The discovery subsystem returns an error
/// * The topology state machine returns an error
fn run_internal<'a>(sm: &mut StateMachine<State, Worker<'a>, SupError>,
                    worker: &mut Worker<'a>)
                    -> Result<()> {
    {
        let package = worker.package.read().unwrap();
        let service_config = worker.service_config.read().unwrap();
        try!(package.create_svc_path());
        try!(package.copy_run(&service_config));
    }
    let handler = wonder::actor::Builder::new(SignalNotifier)
        .name("signal-handler".to_string())
        .start(())
        .unwrap();
    loop {
        let start_time = SteadyTime::now();
        match handler.receiver.try_recv() {
            Ok(wonder::actor::Message::Cast(signals::Message::Signal(sig))) => {
                debug!("SIG = {:?}", sig);
                match sig {
                    signals::Signal::SIGINT | signals::Signal::SIGTERM => {
                        let mut supervisor = worker.supervisor.write().unwrap();
                        try!(supervisor.down());
                        break;
                    }
                    _ => {
                        outputln!("Forwarding {:?} on to the supervised process", sig);
                        let supervisor = worker.supervisor.write().unwrap();
                        try!(supervisor.send_unix_signal(sig.clone()));
                    }
                };
            }
            Ok(_) => {}
            Err(TryRecvError::Empty) => {}
            Err(TryRecvError::Disconnected) => {
                panic!("signal handler crashed!");
            }
        }

        {
            let mut supervisor = worker.supervisor.write().unwrap();
            try!(supervisor.check_process());
        }

        let mut restart_process = false;

        // This section, and the following really need to be refactored:
        //
        // 1. We check to see if we are in an event a bunch of times
        // 2. We potentially restart 3 different times - twice for reconfigure, and once for
        //    file_udpated. That's pretty yucky.
        //
        // Love, Adam
        {
            // Manage the census
            let (write_census, in_event, write_rumor, me_clone) = {
                let cl = worker.census_list.read().unwrap();
                let census = cl.local_census();
                (cl.needs_write(), census.in_event, census.me().needs_write(), census.me().clone())
            };

            if write_census {
                if !in_event {
                    let mut service_config = worker.service_config.write().unwrap();
                    let cl = worker.census_list.read().unwrap();
                    service_config.svc(&cl);
                    service_config.bind(worker.config.bind(), &cl);
                }
                if write_rumor {
                    outputln!("Writing our census rumor: {:#?}", me_clone);
                    let mut rl = worker.rumor_list.write().unwrap();
                    rl.add_rumor(Rumor::census_entry(me_clone));
                }
                let mut cl = worker.census_list.write().unwrap();
                cl.written();
            }

            // Don't bother trying to reconfigure if we are in an event - just wait till
            // everything settles down.
            {
                let census_list = worker.census_list.read().unwrap();
                let census = census_list.local_census();
                if !census.in_event {
                    let mut service_config = worker.service_config.write().unwrap();
                    if service_config.needs_write {
                        let package = worker.package.read().unwrap();
                        // Write the configuration, and restart if needed
                        if try!(service_config.write(&package)) {
                            try!(package.copy_run(&service_config));
                            try!(package.reconfigure(&service_config));
                            outputln!("Restarting because the service config was updated via the \
                                       census");
                            restart_process = true;
                        }
                    }
                }
            }
        }

        {
            let in_event = {
                let census_list = worker.census_list.read().unwrap();
                let census = census_list.local_census();
                census.in_event
            };
            let (needs_file_updated, needs_reconfigure) = if in_event {
                (false, false)
            } else {
                let mut gossip_file_list = worker.gossip_file_list.write().unwrap();
                let needs_write = {
                    gossip_file_list.needs_write()
                };
                if needs_write {
                    try!(gossip_file_list.write())
                } else {
                    (false, false)
                }
            };
            if needs_file_updated {
                let service_config = worker.service_config.read().unwrap();
                let package = worker.package.read().unwrap();
                let existed = try!(package.file_updated(&service_config));
                if !existed {
                    restart_process = true;
                }
            }
            if needs_reconfigure {
                let mut service_config = worker.service_config.write().unwrap();
                let package = worker.package.read().unwrap();
                service_config.cfg(&package);
                if try!(service_config.write(&package)) {
                    try!(package.copy_run(&service_config));
                    let existed = try!(package.reconfigure(&service_config));
                    if !existed {
                        restart_process = true;
                    }
                }
            }
        }

        if let Some(ref updater) = worker.pkg_updater {
            match updater.receiver.try_recv() {
                Ok(wonder::actor::Message::Cast(package::UpdaterMessage::Update(package))) => {
                    debug!("Main loop received package update notification: {:?}",
                           &package);
                    try!(worker.update_package(package));
                    try!(package::PackageUpdater::run(&updater));
                    // force the package to restart
                    outputln!("Restarting because the package was updated");
                    restart_process = true;
                }
                Ok(_) => {}
                Err(TryRecvError::Empty) => {}
                Err(TryRecvError::Disconnected) => {
                    panic!("package updater crashed!");
                }
            }
        }

        {
            let mut supervisor = worker.supervisor.write().unwrap();
            // If our target is that the process is up
            if supervisor.is_up() {
                // And no process is running
                if supervisor.pid.is_none() {
                    // Start a new one
                    try!(supervisor.start());
                } else {
                    // If we were supposed to restart
                    if restart_process {
                        // And we have ever started before...
                        if supervisor.has_started {
                            // Restart
                            try!(supervisor.restart());
                        }
                    }
                }
            }
        }

        // Next state!
        try!(sm.next(worker));

        // Slow down our loop
        let elapsed_time = SteadyTime::now() - start_time;
        let elapsed_millis = elapsed_time.num_milliseconds();

        if elapsed_millis < MINIMUM_LOOP_TIME_MS {
            thread::sleep(Duration::from_millis((MINIMUM_LOOP_TIME_MS - elapsed_millis) as u64));
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};

use toml::Value;
use uuid::Uuid;

use task_hookrs::task::Task as TTask;

use libimagstore::store::{FileLockEntry, Store};
use libimagstore::storeid::{IntoStoreId, StoreIdIterator, StoreId};
use module_path::ModuleEntryPath;

use error::{TodoError, TodoErrorKind};
use result::Result;

/// Task struct containing a `FileLockEntry`
#[derive(Debug)]
pub struct Task<'a>(FileLockEntry<'a>);

impl<'a> Task<'a> {

    /// Concstructs a new `Task` with a `FileLockEntry`
    pub fn new(fle: FileLockEntry<'a>) -> Task<'a> {
        Task(fle)
    }

    pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> {
        store.delete(ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid())
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all(store: &Store) -> Result<TaskIterator> {
        store.retrieve_for_module("todo/taskwarrior")
            .map(|iter| TaskIterator::new(store, iter))
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

}

impl<'a> Deref for Task<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Task<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}

/// A trait to get a `libimagtodo::task::Task` out of the implementing object.
/// This Task struct is merely a wrapper for a `FileLockEntry`, therefore the function name
/// `into_filelockentry`.
pub trait IntoTask<'a> {

    /// # Usage
    /// ```ignore
    /// use std::io::stdin;
    ///
    /// use task_hookrs::task::Task;
    /// use task_hookrs::import::import;
    /// use libimagstore::store::{Store, FileLockEntry};
    ///
    /// if let Ok(task_hookrs_task) = import(stdin()) {
    ///     // Store is given at runtime
    ///     let task = task_hookrs_task.into_filelockentry(store);
    ///     println!("Task with uuid: {}", task.flentry.get_header().get("todo.uuid"));
    /// }
    /// ```
    fn into_filelockentry(self, store : &'a Store) -> Result<Task<'a>>;

}

impl<'a> IntoTask<'a> for TTask {

    fn into_filelockentry(self, store : &'a Store) -> Result<Task<'a>> {
        let uuid     = self.uuid();
        let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid();

        match store.retrieve(store_id) {
            Err(e) => return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(mut fle) => {
                {
                    let mut header = fle.get_header_mut();
                    match header.read("todo") {
                        Ok(None) => {
                            if let Err(e) = header.set("todo", Value::Table(BTreeMap::new())) {
                                return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                            }
                        }
                        Ok(Some(_)) => { }
                        Err(e) => {
                            return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                        }
                    }

                    if let Err(e) = header.set("todo.uuid", Value::String(format!("{}",uuid))) {
                        return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                    }
                }

                // If none of the errors above have returned the function, everything is fine
                Ok(Task::new(fle))
            }
        }
    }

}

trait FromStoreId {
    fn from_storeid<'a>(&'a Store, StoreId) -> Result<Task<'a>>;
}

impl<'a> FromStoreId for Task<'a> {

    fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result<Task<'b>> {
        match store.retrieve(id) {
            Err(e) => Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(c)  => Ok(Task::new( c )),
        }
    }
}

pub struct TaskIterator<'a> {
    store: &'a Store,
    iditer: StoreIdIterator,
}

impl<'a> TaskIterator<'a> {

    pub fn new(store: &'a Store, iditer: StoreIdIterator) -> TaskIterator<'a> {
        TaskIterator {
            store: store,
            iditer: iditer,
        }
    }

}

impl<'a> Iterator for TaskIterator<'a> {
    type Item = Result<Task<'a>>;

    fn next(&mut self) -> Option<Result<Task<'a>>> {
        self.iditer.next().map(|id| Task::from_storeid(self.store, id))
    }
}

<commit_msg>Provide more detailed access to the ids and tasks<commit_after>use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};

use toml::Value;
use uuid::Uuid;

use task_hookrs::task::Task as TTask;

use libimagstore::store::{FileLockEntry, Store};
use libimagstore::storeid::{IntoStoreId, StoreIdIterator, StoreId};
use module_path::ModuleEntryPath;

use error::{TodoError, TodoErrorKind};
use result::Result;

/// Task struct containing a `FileLockEntry`
#[derive(Debug)]
pub struct Task<'a>(FileLockEntry<'a>);

impl<'a> Task<'a> {

    /// Concstructs a new `Task` with a `FileLockEntry`
    pub fn new(fle: FileLockEntry<'a>) -> Task<'a> {
        Task(fle)
    }

    pub fn delete_by_uuid(store: &Store, uuid: Uuid) -> Result<()> {
        store.delete(ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid())
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all_as_ids(store: &Store) -> Result<StoreIdIterator> {
        store.retrieve_for_module("todo/taskwarrior")
            .map_err(|e| TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
    }

    pub fn all(store: &Store) -> Result<TaskIterator> {
        Task::all_as_ids(store)
            .map(|iter| TaskIterator::new(store, iter))
    }

}

impl<'a> Deref for Task<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Task<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}

/// A trait to get a `libimagtodo::task::Task` out of the implementing object.
/// This Task struct is merely a wrapper for a `FileLockEntry`, therefore the function name
/// `into_filelockentry`.
pub trait IntoTask<'a> {

    /// # Usage
    /// ```ignore
    /// use std::io::stdin;
    ///
    /// use task_hookrs::task::Task;
    /// use task_hookrs::import::import;
    /// use libimagstore::store::{Store, FileLockEntry};
    ///
    /// if let Ok(task_hookrs_task) = import(stdin()) {
    ///     // Store is given at runtime
    ///     let task = task_hookrs_task.into_filelockentry(store);
    ///     println!("Task with uuid: {}", task.flentry.get_header().get("todo.uuid"));
    /// }
    /// ```
    fn into_filelockentry(self, store : &'a Store) -> Result<Task<'a>>;

}

impl<'a> IntoTask<'a> for TTask {

    fn into_filelockentry(self, store : &'a Store) -> Result<Task<'a>> {
        let uuid     = self.uuid();
        let store_id = ModuleEntryPath::new(format!("taskwarrior/{}", uuid)).into_storeid();

        match store.retrieve(store_id) {
            Err(e) => return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(mut fle) => {
                {
                    let mut header = fle.get_header_mut();
                    match header.read("todo") {
                        Ok(None) => {
                            if let Err(e) = header.set("todo", Value::Table(BTreeMap::new())) {
                                return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                            }
                        }
                        Ok(Some(_)) => { }
                        Err(e) => {
                            return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                        }
                    }

                    if let Err(e) = header.set("todo.uuid", Value::String(format!("{}",uuid))) {
                        return Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e))))
                    }
                }

                // If none of the errors above have returned the function, everything is fine
                Ok(Task::new(fle))
            }
        }
    }

}

trait FromStoreId {
    fn from_storeid<'a>(&'a Store, StoreId) -> Result<Task<'a>>;
}

impl<'a> FromStoreId for Task<'a> {

    fn from_storeid<'b>(store: &'b Store, id: StoreId) -> Result<Task<'b>> {
        match store.retrieve(id) {
            Err(e) => Err(TodoError::new(TodoErrorKind::StoreError, Some(Box::new(e)))),
            Ok(c)  => Ok(Task::new( c )),
        }
    }
}

pub struct TaskIterator<'a> {
    store: &'a Store,
    iditer: StoreIdIterator,
}

impl<'a> TaskIterator<'a> {

    pub fn new(store: &'a Store, iditer: StoreIdIterator) -> TaskIterator<'a> {
        TaskIterator {
            store: store,
            iditer: iditer,
        }
    }

}

impl<'a> Iterator for TaskIterator<'a> {
    type Item = Result<Task<'a>>;

    fn next(&mut self) -> Option<Result<Task<'a>>> {
        self.iditer.next().map(|id| Task::from_storeid(self.store, id))
    }
}

<|endoftext|>


<commit_before>use client::PluginRegister;
use interface::IrcMessageEvent;

fn on_connect(event: &IrcMessageEvent) {
    let nickserv = &event.client.nickserv;
    if nickserv.enabled {
        if nickserv.account.len() != 0 {
            event.client.send_message(nickserv.name.as_slice(), format!("{} {} {}", nickserv.command, nickserv.account, nickserv.password).as_slice());
        } else {
            event.client.send_message(nickserv.name.as_slice(), format!("{} {}", nickserv.command, nickserv.password).as_slice());
        }
    }

    for channel in event.client.channels.iter() {
        event.client.send_command("JOIN".into_string(), &[channel.as_slice()]);
    }
    event.client.state.write().channels.push_all(event.client.channels.iter().map(|s: &String| s.clone()).collect::<Vec<String>>().as_slice());
}

pub fn register(register: &mut PluginRegister) {
    register.register_irc("004", on_connect);
}
<commit_msg>Add nick change tracking and channel tracking to tracker.rs<commit_after>use client::PluginRegister;
use interface::IrcMessageEvent;

fn on_connect(event: &IrcMessageEvent) {
    let nickserv = &event.client.nickserv;
    if nickserv.enabled {
        if nickserv.account.len() != 0 {
            event.client.send_message(nickserv.name.as_slice(), format!("{} {} {}", nickserv.command, nickserv.account, nickserv.password).as_slice());
        } else {
            event.client.send_message(nickserv.name.as_slice(), format!("{} {}", nickserv.command, nickserv.password).as_slice());
        }
    }

    for channel in event.client.channels.iter() {
        event.client.send_command("JOIN".into_string(), &[channel.as_slice()]);
    }
    event.client.state.write().channels.push_all(event.client.channels.iter().map(|s: &String| s.clone()).collect::<Vec<String>>().as_slice());
}

fn on_join(event: &IrcMessageEvent) {
    if event.channel.is_none() {
        return;
    }
    match event.mask.nick() {
        Some(nick) => if nick == event.client.state.read().nick {
            event.client.state.write().channels.push(event.channel.unwrap().into_string());
        },
        None => (),
    }
}

fn on_nick(event: &IrcMessageEvent) {
    match event.mask.nick() {
        Some(nick) => if nick == event.client.state.read().nick {
            event.client.state.write().nick = event.args[0].into_string();
        },
        None => (),
    }
}

pub fn register(register: &mut PluginRegister) {
    register.register_irc("004", on_connect);
    register.register_irc("join", on_join);
    register.register_irc("nick", on_nick);
}
<|endoftext|>


<commit_before>// Copyright 2014-2016 Johannes Köster, Martin Larralde.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

// Sequence analysis algorithms such as orf finder or GC% counter

// Maybe not the most clever way to do it, but at least runs in o(n)

pub fn gc <'a>(sequence: &'a [u8]) -> f32 {
    let mut n: f32 = 0.0;
    let l: usize = sequence.len();
    let mut i: usize = 0;
    while i < l {
        n += match sequence[i] {
            67 | 71 => 1f32, // G or C
            _       => 0f32,
        } ;
        i += 1
    }
    n / l as f32
}

pub fn gc3 <'a>(sequence: &'a [u8]) -> f32 {
    let mut n: f32 = 0.0;
    let l: usize = sequence.len();
    let mut i: usize = 2;
    while i < l {
        n += match sequence[i] {
            67 | 71 => 1f32,
            _       => 0f32,
        };
        i += 3;
    }
    n / l as f32
}
<commit_msg>Corrected case of magic numbering<commit_after>// Copyright 2014-2016 Johannes Köster, Martin Larralde.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

// Sequence analysis algorithms such as orf finder or GC% counter

// Maybe not the most clever way to do it, but at least runs in o(n)

const LOWER_G:u8 = 103;
const LOWER_C:u8 = 99;

const G:u8 = 71;
const C:u8 = 67;

pub fn gc <'a>(sequence: &'a [u8]) -> f32 {
    let mut count: f32 = 0.0;
    let len: usize = sequence.len();
    let mut i: usize = 0;
    while i < len {
        count += match sequence[i] {
            C|LOWER_C|G|LOWER_G => 1f32, // G or C
                              _ => 0f32,
        } ;
        i += 1
    }
    count / len as f32
}

pub fn gc3 <'a>(sequence: &'a [u8]) -> f32 {
    let mut count: f32 = 0.0;
    let len: usize = sequence.len();
    let mut i: usize = 2;
    while i < len {
        count += match sequence[i] {
            C|LOWER_C|G|LOWER_G => 1f32,
                              _ => 0f32,
        };
        i += 3;
    }
    count / len as f32
}
<|endoftext|>


<commit_before>
<commit_msg>chore(examples): remove unused `demo/util.rs`<commit_after><|endoftext|>