<commit_before>// Copyright 2015 Copyright (c) 2015 Benoît Labaere (benoit.labaere@gmail.com)
//
// Licensed under the MIT license LICENSE or <http://opensource.org/licenses/MIT>
// This file may not be copied, modified, or distributed except according to those terms.

use std::rc::Rc;
use std::sync::mpsc::Sender;
use std::io;
use std::boxed::FnBox;

use mio;

use byteorder::{ BigEndian, WriteBytesExt, ReadBytesExt };

use super::Protocol;
use pipe::*;
use endpoint::*;
use global::*;
use event_loop_msg::SocketEvt;
use EventLoop;
use Message;

pub struct Resp {
    pipe: Option<Pipe>,
    evt_sender: Rc<Sender<SocketEvt>>,
    cancel_send_timeout: Option<Box<FnBox(&mut EventLoop)-> bool>>,
    cancel_recv_timeout: Option<Box<FnBox(&mut EventLoop)-> bool>>,
    backtrace: Vec<u8>,
    ttl: u8
}

impl Resp {
    pub fn new(evt_sender: Rc<Sender<SocketEvt>>) -> Resp {
        Resp {
            pipe: None,
            evt_sender: evt_sender,
            cancel_send_timeout: None,
            cancel_recv_timeout: None,
            backtrace: Vec::with_capacity(64),
            ttl: 8
        }
    }

    fn on_msg_send_ok(&mut self, event_loop: &mut EventLoop) {
        let evt = SocketEvt::MsgSent;
        let timeout = self.cancel_send_timeout.take();

        self.send_event_and_cancel_timeout(event_loop, evt, timeout)
    }

    fn on_msg_send_err(&mut self, event_loop: &mut EventLoop, err: io::Error) {
        let evt = SocketEvt::MsgNotSent(err);
        let timeout = self.cancel_send_timeout.take();

        self.send_event_and_cancel_timeout(event_loop, evt, timeout)
    }

    fn on_msg_recv_ok(&mut self, event_loop: &mut EventLoop, msg: Message) {
        self.save_received_header_to_backtrace(&msg);

        let evt = SocketEvt::MsgRecv(msg);
        let timeout = self.cancel_recv_timeout.take();

        self.send_event_and_cancel_timeout(event_loop, evt, timeout)
    }

    fn save_received_header_to_backtrace(&mut self, msg: &Message) {
        self.backtrace.clear();
        self.backtrace.push_all(msg.get_header());
    }

    fn on_msg_recv_err(&mut self, event_loop: &mut EventLoop, err: io::Error) {
        let evt = SocketEvt::MsgNotRecv(err);
        let timeout = self.cancel_recv_timeout.take();

        self.send_event_and_cancel_timeout(event_loop, evt, timeout)
    }

    fn send_event_and_cancel_timeout(&self,
        event_loop: &mut EventLoop,
        evt: SocketEvt,
        timeout: Option<Box<FnBox(&mut EventLoop)-> bool>>) {

        let _ = self.evt_sender.send(evt);

        timeout.map(|cancel_timeout| cancel_timeout.call_box((event_loop,)));
    }

    fn raw_msg_to_msg(&mut self, pipe_token: mio::Token, raw_msg: Message) -> io::Result<Option<Message>> {
        let (mut header, mut body) = raw_msg.explode();
        let pipe_id = pipe_token.as_usize() as u32;

        header.reserve(4);
        try!(header.write_u32::<BigEndian>(pipe_id));

        let mut hops = 0;
        loop {
            if hops >= self.ttl {
                return Ok(None);
            }

            hops += 1;

            if body.len() < 4 {
                return Ok(None);
            }

            let tail = body.split_off(4);
            header.push_all(&body);

            let position = header.len() - 4;
            if header[position] & 0x80 != 0 {
                let msg = Message::with_header_and_body(header, tail);

                return Ok(Some(msg));
            }
            body = tail;
        }

    }

    fn msg_to_raw_msg(&mut self, msg: Message) -> io::Result<(Message, mio::Token)> {
        let (mut header, body) = msg.explode();
        let pipe_id_bytes = vec!(
            self.backtrace[0],
            self.backtrace[1],
            self.backtrace[2],
            self.backtrace[3]
        );
        let mut pipe_id_reader = io::Cursor::new(pipe_id_bytes);
        let pipe_id = try!(pipe_id_reader.read_u32::<BigEndian>());
        let pipe_token = mio::Token(pipe_id as usize);

        self.restore_saved_backtrace_to_header(&mut header);
        self.backtrace.clear();

        Ok((Message::with_header_and_body(header, body), pipe_token))
    }

    fn restore_saved_backtrace_to_header(&self, header: &mut Vec<u8>) {
        let backtrace = &self.backtrace[4..];

        header.reserve(backtrace.len());
        header.push_all(backtrace);
    }

    fn on_raw_msg_recv(&mut self, event_loop: &mut EventLoop, pipe_token: mio::Token, raw_msg: Message) {
        match self.raw_msg_to_msg(pipe_token, raw_msg) {
            Ok(None)      => {}
            Ok(Some(msg)) => self.on_msg_recv_ok(event_loop, msg),
            Err(e)        => self.on_msg_recv_err(event_loop, e)
        }
    }
}

impl Protocol for Resp {
    fn id(&self) -> u16 {
        SocketType::Respondent.id()
    }

    fn peer_id(&self) -> u16 {
        SocketType::Surveyor.id()
    }

    fn add_endpoint(&mut self, token: mio::Token, endpoint: Endpoint) {
        self.pipe = Some(Pipe::new(token, endpoint));
    }

    fn remove_endpoint(&mut self, token: mio::Token) -> Option<Endpoint> {
        if Some(token) == self.pipe.as_ref().map(|p| p.token()) {
            self.pipe.take().map(|p| p.remove())
        } else {
            None
        }
    }

    fn ready(&mut self, event_loop: &mut EventLoop, token: mio::Token, events: mio::EventSet) -> io::Result<()> {
        if self.pipe.is_none() {
            return Ok(());
        }
        if Some(token) != self.pipe.as_ref().map(|p| p.token()) {
            return Ok(());
        }

        let mut pipe = self.pipe.take().unwrap();
        let mut msg_sending = false;
        let mut msg_sent = false;
        let mut received_msg = None;
        let mut receiving_msg = false;

        let (sent, received) = try!(pipe.ready(event_loop, events));

        if sent {
            msg_sent = true;
        } else {
            match try!(pipe.resume_pending_send()) {
                Some(true)  => msg_sent = true,
                Some(false) => msg_sending = true,
                None        => {}
            }
        }

        if received.is_some() {
            received_msg = received;
        } else {
            match try!(pipe.resume_pending_recv()) {
                Some(RecvStatus::Completed(msg))   => received_msg = Some(msg),
                Some(RecvStatus::InProgress)       => receiving_msg = true,
                Some(RecvStatus::Postponed) | None => {}
            }
        }

        if msg_sent {
            self.on_msg_send_ok(event_loop);
        }

        if msg_sending | msg_sent {
            pipe.reset_pending_send();
        }

        if received_msg.is_some() | receiving_msg {
            pipe.reset_pending_recv();
        }

        if received_msg.is_some() {
            let raw_msg = received_msg.unwrap();

            self.on_raw_msg_recv(event_loop, token, raw_msg);
        }

        self.pipe = Some(pipe);

        Ok(())
    }

    fn send(&mut self, event_loop: &mut EventLoop, msg: Message, cancel_timeout: Box<FnBox(&mut EventLoop)-> bool>) {
        if self.pipe.is_none() {
            let err = io::Error::new(io::ErrorKind::NotConnected, "no connected endpoint");

            self.on_msg_send_err(event_loop, err);

            return;
        }

        let mut pipe = self.pipe.take().unwrap();

        self.cancel_send_timeout = Some(cancel_timeout);

        let (raw_msg, pipe_token) = match self.msg_to_raw_msg(msg) {
            Err(e) => {
                self.on_msg_send_err(event_loop, e);
                return;
            },
            Ok((raw_msg, pipe_token)) => (raw_msg, pipe_token)
        };

        if pipe_token != pipe.token() {
            let err = io::Error::new(io::ErrorKind::NotConnected, "original surveyor disconnected");

            self.on_msg_send_err(event_loop, err);

            return;
        }

        let mut sent = false;
        let mut sending = false;
        let mut pending = false;

        match pipe.send(Rc::new(raw_msg)) {
            Ok(Some(true))  => sent = true,
            Ok(Some(false)) => sending = true,
            Ok(None)        => pending = true,
            Err(_)          => {}
            // this pipe looks dead, but it will be taken care of during next ready notification
        }

        if sent {
            self.on_msg_send_ok(event_loop);
        }

        if sent | sending {
            pipe.reset_pending_send();
        } else if pending == false {
            let err = io::Error::new(io::ErrorKind::NotConnected, "no connected endpoint");

            self.on_msg_send_err(event_loop, err);
        }

        self.pipe = Some(pipe);
    }

    fn on_send_timeout(&mut self, event_loop: &mut EventLoop) {
        let err = io::Error::new(io::ErrorKind::TimedOut, "send timeout reached");

        self.on_msg_send_err(event_loop, err);

        if let Some(pipe) = self.pipe.as_mut() {
            pipe.on_send_timeout();
        }
    }

    fn recv(&mut self, event_loop: &mut EventLoop, cancel_timeout: Box<FnBox(&mut EventLoop)-> bool>) {
        if self.pipe.is_none() {
            let err = io::Error::new(io::ErrorKind::NotConnected, "no connected endpoint");

            self.on_msg_send_err(event_loop, err);

            return;
        }

        let mut pipe = self.pipe.take().unwrap();

        self.cancel_recv_timeout = Some(cancel_timeout);

        let mut received = None;
        let mut receiving = false;
        let mut pending = false;

        match pipe.recv() {
            Ok(RecvStatus::Completed(msg)) => received = Some(msg),
            Ok(RecvStatus::InProgress)     => receiving = true,
            Ok(RecvStatus::Postponed)      => pending = true,
            Err(_)                         => {}
        }

        if received.is_some() | receiving {
            pipe.reset_pending_recv();
        } else if pending == false {
            let err = io::Error::new(io::ErrorKind::NotConnected, "no connected endpoint");

            self.on_msg_recv_err(event_loop, err);
        }

        if received.is_some() {
            let pipe_token = pipe.token();
            let raw_msg = received.unwrap();

            self.on_raw_msg_recv(event_loop, pipe_token, raw_msg);
        }

        self.pipe = Some(pipe);
    }

    fn on_recv_timeout(&mut self, event_loop: &mut EventLoop) {
        let err = io::Error::new(io::ErrorKind::TimedOut, "recv timeout reached");

        self.on_msg_recv_err(event_loop, err);

        if let Some(pipe) = self.pipe.as_mut() {
            pipe.on_recv_timeout();
        }
    }
}
<commit_msg>Goes on moving pending operation<commit_after>// Copyright 2015 Copyright (c) 2015 Benoît Labaere (benoit.labaere@gmail.com)
//
// Licensed under the MIT license LICENSE or <http://opensource.org/licenses/MIT>
// This file may not be copied, modified, or distributed except according to those terms.

use std::rc::Rc;
use std::sync::mpsc::Sender;
use std::io;
use std::boxed::FnBox;

use mio;

use byteorder::{ BigEndian, WriteBytesExt, ReadBytesExt };

use super::Protocol;
use pipe2::*;
use endpoint::*;
use global::*;
use event_loop_msg::SocketEvt;
use EventLoop;
use EventLoopAction;
use Message;

pub struct Resp {
    pipe: Option<Pipe>,
    evt_sender: Rc<Sender<SocketEvt>>,
    cancel_send_timeout: Option<EventLoopAction>,
    cancel_recv_timeout: Option<EventLoopAction>,
    pending_send: Option<Rc<Message>>,
    pending_recv: bool,
    backtrace: Vec<u8>,
    ttl: u8
}

impl Resp {
    pub fn new(evt_sender: Rc<Sender<SocketEvt>>) -> Resp {
        Resp {
            pipe: None,
            evt_sender: evt_sender,
            cancel_send_timeout: None,
            cancel_recv_timeout: None,
            pending_send: None,
            pending_recv: false,
            backtrace: Vec::with_capacity(64),
            ttl: 8
        }
    }

    fn on_msg_send_finished(&mut self, event_loop: &mut EventLoop, evt: SocketEvt) {
        let _ = self.evt_sender.send(evt);
        let timeout = self.cancel_send_timeout.take();

        timeout.map(|cancel_timeout| cancel_timeout.call_box((event_loop,)));

        self.pending_send = None;
    }

    fn on_msg_send_finished_ok(&mut self, event_loop: &mut EventLoop) {
        self.on_msg_send_finished(event_loop, SocketEvt::MsgSent);

        if let Some(pipe) = self.pipe.as_mut() {
            pipe.finish_send();
        }
    }

    fn on_msg_send_finished_err(&mut self, event_loop: &mut EventLoop, err: io::Error) {
        self.on_msg_send_finished(event_loop, SocketEvt::MsgNotSent(err));

        if let Some(pipe) = self.pipe.as_mut() {
            pipe.cancel_send();
        }
    }

    fn on_msg_send_started(&mut self) {
        self.pending_send = None;
    }

    fn process_send_result(&mut self, event_loop: &mut EventLoop, sent: bool, sending: bool) {
        if sent {
            self.on_msg_send_finished_ok(event_loop);
        } else if sending {
            self.on_msg_send_started();
        }
    }

    fn on_msg_recv_finished(&mut self, event_loop: &mut EventLoop, evt: SocketEvt) {
        let _ = self.evt_sender.send(evt);
        let timeout = self.cancel_recv_timeout.take();

        timeout.map(|cancel_timeout| cancel_timeout.call_box((event_loop,)));

        self.pending_recv = false;
    }

    fn on_msg_recv_finished_ok(&mut self, event_loop: &mut EventLoop, msg: Message) {
        self.save_received_header_to_backtrace(&msg);
        self.on_msg_recv_finished(event_loop, SocketEvt::MsgRecv(msg));

        if let Some(pipe) = self.pipe.as_mut() {
            pipe.finish_recv();
        }
    }

    fn on_msg_recv_finished_err(&mut self, event_loop: &mut EventLoop, err: io::Error) {
        self.on_msg_recv_finished(event_loop, SocketEvt::MsgNotRecv(err));

        if let Some(pipe) = self.pipe.as_mut() {
            pipe.cancel_recv();
        }
    }

    fn on_msg_recv_started(&mut self, _: mio::Token) {
        self.pending_recv = false;
    }

    fn process_recv_result(
        &mut self,
        event_loop: &mut EventLoop,
        received: Option<(Message, mio::Token)>,
        receiving: Option<mio::Token>) {

        if let Some((raw_msg, from)) = received {
            self.on_raw_msg_recv_from(event_loop, raw_msg, from);
        }
        else if let Some(token) = receiving {
            self.on_msg_recv_started(token);
        }
    }

    fn on_raw_msg_recv_from(&mut self, event_loop: &mut EventLoop, raw_msg: Message, pipe_token: mio::Token) {
        match self.raw_msg_to_msg(pipe_token, raw_msg) {
            Ok(None)      => {}
            Ok(Some(msg)) => self.on_msg_recv_finished_ok(event_loop, msg),
            Err(e)        => self.on_msg_recv_finished_err(event_loop, e)
        }
    }

    fn save_received_header_to_backtrace(&mut self, msg: &Message) {
        self.backtrace.clear();
        self.backtrace.push_all(msg.get_header());
    }

    fn raw_msg_to_msg(&mut self, pipe_token: mio::Token, raw_msg: Message) -> io::Result<Option<Message>> {
        let (mut header, mut body) = raw_msg.explode();
        let pipe_id = pipe_token.as_usize() as u32;

        header.reserve(4);
        try!(header.write_u32::<BigEndian>(pipe_id));

        let mut hops = 0;
        loop {
            if hops >= self.ttl {
                return Ok(None);
            }

            hops += 1;

            if body.len() < 4 {
                return Ok(None);
            }

            let tail = body.split_off(4);
            header.push_all(&body);

            let position = header.len() - 4;
            if header[position] & 0x80 != 0 {
                let msg = Message::with_header_and_body(header, tail);

                return Ok(Some(msg));
            }
            body = tail;
        }

    }

    fn msg_to_raw_msg(&mut self, msg: Message) -> io::Result<(Message, mio::Token)> {
        let (mut header, body) = msg.explode();
        let pipe_id_bytes = vec!(
            self.backtrace[0],
            self.backtrace[1],
            self.backtrace[2],
            self.backtrace[3]
        );
        let mut pipe_id_reader = io::Cursor::new(pipe_id_bytes);
        let pipe_id = try!(pipe_id_reader.read_u32::<BigEndian>());
        let pipe_token = mio::Token(pipe_id as usize);

        self.restore_saved_backtrace_to_header(&mut header);
        self.backtrace.clear();

        Ok((Message::with_header_and_body(header, body), pipe_token))
    }

    fn restore_saved_backtrace_to_header(&self, header: &mut Vec<u8>) {
        let backtrace = &self.backtrace[4..];

        header.reserve(backtrace.len());
        header.push_all(backtrace);
    }
}

impl Protocol for Resp {
    fn id(&self) -> u16 {
        SocketType::Respondent.id()
    }

    fn peer_id(&self) -> u16 {
        SocketType::Surveyor.id()
    }

    fn add_endpoint(&mut self, token: mio::Token, endpoint: Endpoint) {
        self.pipe = Some(Pipe::new(token, endpoint));
    }

    fn remove_endpoint(&mut self, token: mio::Token) -> Option<Endpoint> {
        if Some(token) == self.pipe.as_ref().map(|p| p.token()) {
            self.pipe.take().map(|p| p.remove())
        } else {
            None
        }
    }

    fn send(&mut self, event_loop: &mut EventLoop, msg: Message, cancel_timeout: EventLoopAction) {
        self.cancel_send_timeout = Some(cancel_timeout);

        if self.pipe.is_none() {
            self.pending_send = Some(Rc::new(msg));
            return;
        }

        let (raw_msg, pipe_token) = match self.msg_to_raw_msg(msg) {
            Err(e) => return self.on_msg_send_finished_err(event_loop, e),
            Ok((raw_msg, pipe_token)) => (raw_msg, pipe_token)
        };

        let mut pipe = self.pipe.take().unwrap();
        let mut sent = false;
        let mut sending = false;
        let msg = Rc::new(raw_msg);

        if pipe_token != pipe.token() {
            let err = io::Error::new(io::ErrorKind::NotConnected, "original surveyor disconnected");

            self.on_msg_send_finished_err(event_loop, err);
            self.pipe = Some(pipe);

            return;
        }

        match pipe.send(msg.clone()) {
            Ok(SendStatus::Completed)  => sent = true,
            Ok(SendStatus::InProgress) => sending = true,
            _ => {}
        }

        self.pipe = Some(pipe);
        self.pending_send = Some(msg);
        self.process_send_result(event_loop, sent, sending);
    }

    fn on_send_timeout(&mut self, event_loop: &mut EventLoop) {
        let err = io::Error::new(io::ErrorKind::TimedOut, "send timeout reached");

        self.on_msg_send_finished_err(event_loop, err);
    }

    fn recv(&mut self, event_loop: &mut EventLoop, cancel_timeout: EventLoopAction) {
        self.cancel_recv_timeout = Some(cancel_timeout);

        if self.pipe.is_none() {
            self.pending_recv = true;
            return;
        }

        let mut pipe = self.pipe.take().unwrap();
        let mut received = None;
        let mut receiving = None;

        match pipe.recv() {
            Ok(RecvStatus::Completed(msg)) => received = Some((msg, pipe.token())),
            Ok(RecvStatus::InProgress)     => receiving = Some(pipe.token()),
            _ => {}
        }

        self.pipe = Some(pipe);
        self.pending_recv = true;
        self.process_recv_result(event_loop, received, receiving);
    }

    fn on_recv_timeout(&mut self, event_loop: &mut EventLoop) {
        let err = io::Error::new(io::ErrorKind::TimedOut, "recv timeout reached");

        self.on_msg_recv_finished_err(event_loop, err);
    }

    fn ready(&mut self, event_loop: &mut EventLoop, token: mio::Token, events: mio::EventSet) -> io::Result<()> {
        if self.pipe.is_none() {
            return Ok(());
        }
        if Some(token) != self.pipe.as_ref().map(|p| p.token()) {
            return Ok(());
        }
        let mut sent = false;
        let mut sending = false;
        let mut received = None;
        let mut receiving = None;

        if let Some(pipe) = self.pipe.as_mut() {
            let has_pending_send = self.pending_send.is_some();
            let has_pending_recv = self.pending_recv;
            let (s, r) = try!(pipe.ready(event_loop, events));
            sent = s;
            received = r.map(|msg| (msg, pipe.token()));

            if has_pending_send && !sent && pipe.can_resume_send() {
                let msg = self.pending_send.as_ref().unwrap();

                match try!(pipe.send(msg.clone())) {
                    SendStatus::Completed  => sent = true,
                    SendStatus::InProgress => sending = true,
                    _ => {}
                }
            }

            if has_pending_recv && received.is_none() && pipe.can_resume_recv() {
                match try!(pipe.recv()) {
                    RecvStatus::Completed(msg) => received = Some((msg, pipe.token())),
                    RecvStatus::InProgress     => receiving = Some(token),
                    _ => {}
                }
            }
        }

        self.process_send_result(event_loop, sent, sending);
        self.process_recv_result(event_loop, received, receiving);

        Ok(())
        /*let mut pipe = self.pipe.take().unwrap();
        let mut msg_sending = false;
        let mut msg_sent = false;
        let mut received_msg = None;
        let mut receiving_msg = false;

        let (sent, received) = try!(pipe.ready(event_loop, events));

        if sent {
            msg_sent = true;
        } else {
            match try!(pipe.resume_pending_send()) {
                Some(true)  => msg_sent = true,
                Some(false) => msg_sending = true,
                None        => {}
            }
        }

        if received.is_some() {
            received_msg = received;
        } else {
            match try!(pipe.resume_pending_recv()) {
                Some(RecvStatus::Completed(msg))   => received_msg = Some(msg),
                Some(RecvStatus::InProgress)       => receiving_msg = true,
                Some(RecvStatus::Postponed) | None => {}
            }
        }

        if msg_sent {
            self.on_msg_send_ok(event_loop);
        }

        if msg_sending | msg_sent {
            pipe.reset_pending_send();
        }

        if received_msg.is_some() | receiving_msg {
            pipe.reset_pending_recv();
        }

        if received_msg.is_some() {
            let raw_msg = received_msg.unwrap();

            self.on_raw_msg_recv(event_loop, token, raw_msg);
        }

        self.pipe = Some(pipe);
*/
    }
}
<|endoftext|>


<commit_before>//! Local server that accepts SOCKS 5 protocol

use std::{io, net::SocketAddr, sync::Arc};

use futures::{self, stream::Stream, Future};

use tokio::{
    self,
    net::{TcpListener, TcpStream},
};
use tokio_io::{
    io::{flush, ReadHalf, WriteHalf},
    AsyncRead,
};

use config::ServerConfig;
use context::SharedContext;

use relay::{
    boxed_future,
    loadbalancing::server::{LoadBalancer, RoundRobin},
    socks5::{self, Address, HandshakeRequest, HandshakeResponse, TcpRequestHeader, TcpResponseHeader},
    tcprelay::crypto_io::{DecryptedRead, EncryptedWrite},
};

use super::{ignore_until_end, try_timeout, tunnel};

#[derive(Debug, Clone)]
struct UdpConfig {
    enable_udp: bool,
    client_addr: SocketAddr,
}

fn handle_socks5_connect(
    context: SharedContext,
    (r, w): (ReadHalf<TcpStream>, WriteHalf<TcpStream>),
    client_addr: SocketAddr,
    addr: Address,
    svr_cfg: Arc<ServerConfig>,
) -> impl Future<Item = (), Error = io::Error> + Send {
    let timeout = svr_cfg.timeout();
    super::connect_proxy_server(context, svr_cfg.clone())
        .then(move |res| {
            let (header, r) = match res {
                Ok(svr_s) => {
                    trace!("Proxy server connected");

                    // Tell the client that we are ready
                    let header = TcpResponseHeader::new(socks5::Reply::Succeeded, Address::SocketAddress(client_addr));

                    (header, Ok(svr_s))
                }
                Err(err) => {
                    use relay::socks5::Reply;
                    use std::io::ErrorKind;

                    error!("Failed to connect remote server, err: {}", err);

                    let reply = match err.kind() {
                        ErrorKind::ConnectionRefused => Reply::ConnectionRefused,
                        ErrorKind::ConnectionAborted => Reply::HostUnreachable,
                        _ => Reply::NetworkUnreachable,
                    };

                    let header = TcpResponseHeader::new(reply, Address::SocketAddress(client_addr));

                    (header, Err(err))
                }
            };

            trace!("Sent header: {:?}", header);
            try_timeout(header.write_to(w).and_then(flush), timeout).and_then(|w| match r {
                Ok(svr_s) => Ok((svr_s, w)),
                Err(err) => Err(err),
            })
        })
        .and_then(move |(svr_s, w)| {
            let timeout = svr_cfg.timeout();
            super::proxy_server_handshake(svr_s, svr_cfg, addr).and_then(move |(svr_r, svr_w)| {
                let rhalf = svr_r.and_then(move |svr_r| svr_r.copy_timeout_opt(w, timeout));
                let whalf = svr_w.and_then(move |svr_w| svr_w.copy_timeout_opt(r, timeout));
                tunnel(whalf, rhalf)
            })
        })
}

fn handle_socks5_client(
    context: SharedContext,
    s: TcpStream,
    conf: Arc<ServerConfig>,
    udp_conf: UdpConfig,
) -> io::Result<()> {
    if let Err(err) = s.set_keepalive(conf.timeout()) {
        error!("Failed to set keep alive: {:?}", err);
    }

    if let Err(err) = s.set_nodelay(true) {
        error!("Failed to set no delay: {:?}", err);
    }

    let client_addr = s.peer_addr()?;
    let cloned_client_addr = client_addr;
    let fut = futures::lazy(|| Ok(s.split()))
        .and_then(|(r, w)| {
            // Socks5 handshakes
            HandshakeRequest::read_from(r).and_then(move |(r, req)| {
                trace!("Socks5 {:?}", req);

                let (resp, res) = if !req.methods.contains(&socks5::SOCKS5_AUTH_METHOD_NONE) {
                    let resp = HandshakeResponse::new(socks5::SOCKS5_AUTH_METHOD_NOT_ACCEPTABLE);
                    warn!("Currently shadowsocks-rust does not support authentication");
                    (
                        resp,
                        Err(io::Error::new(
                            io::ErrorKind::Other,
                            "Currently shadowsocks-rust does not support authentication",
                        )),
                    )
                } else {
                    // Reply to client
                    let resp = HandshakeResponse::new(socks5::SOCKS5_AUTH_METHOD_NONE);
                    trace!("Reply handshake {:?}", resp);
                    (resp, Ok(()))
                };

                resp.write_to(w).and_then(flush).and_then(move |w| match res {
                    Ok(..) => Ok((r, w)),
                    Err(err) => Err(err),
                })
            })
        })
        .and_then(move |(r, w)| {
            // Fetch headers
            TcpRequestHeader::read_from(r).then(move |res| match res {
                Ok((r, h)) => boxed_future(futures::finished((r, w, h))),
                Err(err) => {
                    error!("Failed to get TcpRequestHeader: {}", err);
                    let fut = TcpResponseHeader::new(err.reply, Address::SocketAddress(client_addr))
                        .write_to(w)
                        .then(|_| Err(From::from(err)));
                    boxed_future(fut)
                }
            })
        })
        .and_then(move |(r, w, header)| {
            trace!("Socks5 {:?}", header);

            let addr = header.address;
            match header.command {
                socks5::Command::TcpConnect => {
                    let enable_tcp = context.config().mode.enable_tcp();
                    if enable_tcp {
                        debug!("CONNECT {}", addr);
                        let fut = handle_socks5_connect(context, (r, w), cloned_client_addr, addr.clone(), conf)
                            .map_err(move |err| {
                                error!("CONNECT {} failed with error: {}", addr, err);
                                err
                            });
                        boxed_future(fut)
                    } else {
                        warn!("CONNECT is not enabled");
                        let fut = TcpResponseHeader::new(socks5::Reply::CommandNotSupported, addr)
                            .write_to(w)
                            .map(|_| ());
                        boxed_future(fut)
                    }
                }
                socks5::Command::TcpBind => {
                    warn!("BIND is not supported");
                    let fut = TcpResponseHeader::new(socks5::Reply::CommandNotSupported, addr)
                        .write_to(w)
                        .map(|_| ());
                    boxed_future(fut)
                }
                socks5::Command::UdpAssociate => {
                    if udp_conf.enable_udp {
                        debug!("UDP ASSOCIATE {}", addr);
                        let fut = TcpResponseHeader::new(socks5::Reply::Succeeded, From::from(udp_conf.client_addr))
                            .write_to(w)
                            .and_then(flush)
                            .and_then(|_| {
                                // Hold the connection until it ends by its own
                                ignore_until_end(r).map(|_| ())
                            });

                        boxed_future(fut)
                    } else {
                        warn!("UDP ASSOCIATE is not enabled");
                        let fut = TcpResponseHeader::new(socks5::Reply::CommandNotSupported, addr)
                            .write_to(w)
                            .map(|_| ());
                        boxed_future(fut)
                    }
                }
            }
        });

    tokio::spawn(fut.then(|res| match res {
        Ok(..) => Ok(()),
        Err(err) => {
            if err.kind() != io::ErrorKind::BrokenPipe {
                error!("Failed to handle client: {}", err);
            }
            Err(())
        }
    }));

    Ok(())
}

/// Starts a TCP local server with Socks5 proxy protocol
pub fn run(context: SharedContext) -> impl Future<Item = (), Error = io::Error> + Send {
    let local_addr = *context.config().local.as_ref().expect("Missing local config");

    let listener = TcpListener::bind(&local_addr).unwrap_or_else(|err| panic!("Failed to listen, {}", err));

    info!("ShadowSocks TCP Listening on {}", local_addr);

    let udp_conf = UdpConfig {
        enable_udp: context.config().mode.enable_udp(),
        client_addr: local_addr,
    };

    let mut servers = RoundRobin::new(context.config());
    listener.incoming().for_each(move |socket| {
        let server_cfg = servers.pick_server();

        trace!("Got connection, addr: {}", socket.peer_addr()?);
        trace!("Picked proxy server: {:?}", server_cfg);

        handle_socks5_client(context.clone(), socket, server_cfg, udp_conf.clone())
    })
}
<commit_msg>Optimized error log in local<commit_after>//! Local server that accepts SOCKS 5 protocol

use std::{io, net::SocketAddr, sync::Arc};

use futures::{self, stream::Stream, Future};

use tokio::{
    self,
    net::{TcpListener, TcpStream},
};
use tokio_io::{
    io::{flush, ReadHalf, WriteHalf},
    AsyncRead,
};

use config::ServerConfig;
use context::SharedContext;

use relay::{
    boxed_future,
    loadbalancing::server::{LoadBalancer, RoundRobin},
    socks5::{self, Address, HandshakeRequest, HandshakeResponse, TcpRequestHeader, TcpResponseHeader},
    tcprelay::crypto_io::{DecryptedRead, EncryptedWrite},
};

use super::{ignore_until_end, try_timeout, tunnel};

#[derive(Debug, Clone)]
struct UdpConfig {
    enable_udp: bool,
    client_addr: SocketAddr,
}

fn handle_socks5_connect(
    context: SharedContext,
    (r, w): (ReadHalf<TcpStream>, WriteHalf<TcpStream>),
    client_addr: SocketAddr,
    addr: Address,
    svr_cfg: Arc<ServerConfig>,
) -> impl Future<Item = (), Error = io::Error> + Send {
    let timeout = svr_cfg.timeout();
    super::connect_proxy_server(context, svr_cfg.clone())
        .then(move |res| {
            let (header, r) = match res {
                Ok(svr_s) => {
                    trace!("Proxy server connected");

                    // Tell the client that we are ready
                    let header = TcpResponseHeader::new(socks5::Reply::Succeeded, Address::SocketAddress(client_addr));

                    (header, Ok(svr_s))
                }
                Err(err) => {
                    use relay::socks5::Reply;
                    use std::io::ErrorKind;

                    error!("Failed to connect remote server, err: {}", err);

                    let reply = match err.kind() {
                        ErrorKind::ConnectionRefused => Reply::ConnectionRefused,
                        ErrorKind::ConnectionAborted => Reply::HostUnreachable,
                        _ => Reply::NetworkUnreachable,
                    };

                    let header = TcpResponseHeader::new(reply, Address::SocketAddress(client_addr));

                    (header, Err(err))
                }
            };

            trace!("Sent header: {:?}", header);
            try_timeout(header.write_to(w).and_then(flush), timeout).and_then(|w| match r {
                Ok(svr_s) => Ok((svr_s, w)),
                Err(err) => Err(err),
            })
        })
        .and_then(move |(svr_s, w)| {
            let timeout = svr_cfg.timeout();
            super::proxy_server_handshake(svr_s, svr_cfg, addr).and_then(move |(svr_r, svr_w)| {
                let rhalf = svr_r.and_then(move |svr_r| svr_r.copy_timeout_opt(w, timeout));
                let whalf = svr_w.and_then(move |svr_w| svr_w.copy_timeout_opt(r, timeout));
                tunnel(whalf, rhalf)
            })
        })
}

fn handle_socks5_client(
    context: SharedContext,
    s: TcpStream,
    conf: Arc<ServerConfig>,
    udp_conf: UdpConfig,
) -> io::Result<()> {
    if let Err(err) = s.set_keepalive(conf.timeout()) {
        error!("Failed to set keep alive: {:?}", err);
    }

    if let Err(err) = s.set_nodelay(true) {
        error!("Failed to set no delay: {:?}", err);
    }

    let client_addr = s.peer_addr()?;
    let cloned_client_addr = client_addr;
    let fut = futures::lazy(|| Ok(s.split()))
        .and_then(|(r, w)| {
            // Socks5 handshakes
            HandshakeRequest::read_from(r).and_then(move |(r, req)| {
                trace!("Socks5 {:?}", req);

                let (resp, res) = if !req.methods.contains(&socks5::SOCKS5_AUTH_METHOD_NONE) {
                    let resp = HandshakeResponse::new(socks5::SOCKS5_AUTH_METHOD_NOT_ACCEPTABLE);
                    warn!("Currently shadowsocks-rust does not support authentication");
                    (
                        resp,
                        Err(io::Error::new(
                            io::ErrorKind::Other,
                            "Currently shadowsocks-rust does not support authentication",
                        )),
                    )
                } else {
                    // Reply to client
                    let resp = HandshakeResponse::new(socks5::SOCKS5_AUTH_METHOD_NONE);
                    trace!("Reply handshake {:?}", resp);
                    (resp, Ok(()))
                };

                resp.write_to(w).and_then(flush).and_then(move |w| match res {
                    Ok(..) => Ok((r, w)),
                    Err(err) => Err(err),
                })
            })
        })
        .and_then(move |(r, w)| {
            // Fetch headers
            TcpRequestHeader::read_from(r).then(move |res| match res {
                Ok((r, h)) => boxed_future(futures::finished((r, w, h))),
                Err(err) => {
                    error!("Failed to get TcpRequestHeader: {}", err);
                    let fut = TcpResponseHeader::new(err.reply, Address::SocketAddress(client_addr))
                        .write_to(w)
                        .then(|_| Err(From::from(err)));
                    boxed_future(fut)
                }
            })
        })
        .and_then(move |(r, w, header)| {
            trace!("Socks5 {:?}", header);

            let addr = header.address;
            match header.command {
                socks5::Command::TcpConnect => {
                    let enable_tcp = context.config().mode.enable_tcp();
                    if enable_tcp {
                        debug!("CONNECT {}", addr);
                        let fut = handle_socks5_connect(context, (r, w), cloned_client_addr, addr.clone(), conf)
                            .map_err(move |err| {
                                io::Error::new(err.kind(), format!("CONNECT {} failed with error \"{}\"", addr, err))
                            });
                        boxed_future(fut)
                    } else {
                        warn!("CONNECT is not enabled");
                        let fut = TcpResponseHeader::new(socks5::Reply::CommandNotSupported, addr)
                            .write_to(w)
                            .map(|_| ());
                        boxed_future(fut)
                    }
                }
                socks5::Command::TcpBind => {
                    warn!("BIND is not supported");
                    let fut = TcpResponseHeader::new(socks5::Reply::CommandNotSupported, addr)
                        .write_to(w)
                        .map(|_| ());
                    boxed_future(fut)
                }
                socks5::Command::UdpAssociate => {
                    if udp_conf.enable_udp {
                        debug!("UDP ASSOCIATE {}", addr);
                        let fut = TcpResponseHeader::new(socks5::Reply::Succeeded, From::from(udp_conf.client_addr))
                            .write_to(w)
                            .and_then(flush)
                            .and_then(|_| {
                                // Hold the connection until it ends by its own
                                ignore_until_end(r).map(|_| ())
                            });

                        boxed_future(fut)
                    } else {
                        warn!("UDP ASSOCIATE is not enabled");
                        let fut = TcpResponseHeader::new(socks5::Reply::CommandNotSupported, addr)
                            .write_to(w)
                            .map(|_| ());
                        boxed_future(fut)
                    }
                }
            }
        });

    tokio::spawn(fut.then(|res| match res {
        Ok(..) => Ok(()),
        Err(err) => {
            if err.kind() != io::ErrorKind::BrokenPipe {
                error!("Socks5 client: {}", err);
            }
            Err(())
        }
    }));

    Ok(())
}

/// Starts a TCP local server with Socks5 proxy protocol
pub fn run(context: SharedContext) -> impl Future<Item = (), Error = io::Error> + Send {
    let local_addr = *context.config().local.as_ref().expect("Missing local config");

    let listener =
        TcpListener::bind(&local_addr).unwrap_or_else(|err| panic!("Failed to listen on {}, {}", local_addr, err));

    info!("ShadowSocks TCP Listening on {}", local_addr);

    let udp_conf = UdpConfig {
        enable_udp: context.config().mode.enable_udp(),
        client_addr: local_addr,
    };

    let mut servers = RoundRobin::new(context.config());
    listener.incoming().for_each(move |socket| {
        let server_cfg = servers.pick_server();

        trace!("Got connection, addr: {}", socket.peer_addr()?);
        trace!("Picked proxy server: {:?}", server_cfg);

        handle_socks5_client(context.clone(), socket, server_cfg, udp_conf.clone())
    })
}
<|endoftext|>


<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 euclid::size::TypedSize2D;
use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, PipelineId};
use pipeline::Pipeline;
use script_traits::LoadData;
use std::collections::HashMap;
use std::iter::once;
use std::mem::replace;
use std::time::Instant;
use style_traits::CSSPixel;

/// The constellation's view of a browsing context.
/// Each browsing context has a session history, caused by
/// navigation and traversing the history. Each browsing contest has its
/// current entry, plus past and future entries. The past is sorted
/// chronologically, the future is sorted reverse chronologically:
/// in particular prev.pop() is the latest past entry, and
/// next.pop() is the earliest future entry.
pub struct BrowsingContext {
    /// The browsing context id.
    pub id: BrowsingContextId,

    /// The top-level browsing context ancestor
    pub top_level_id: TopLevelBrowsingContextId,

    /// The size of the frame.
    pub size: Option<TypedSize2D<f32, CSSPixel>>,

    /// The timestamp for the current session history entry.
    pub instant: Instant,

    /// The pipeline for the current session history entry.
    pub pipeline_id: PipelineId,

    /// The load data for the current session history entry.
    pub load_data: LoadData,

    /// The past session history, ordered chronologically.
    pub prev: Vec<SessionHistoryEntry>,

    /// The future session history, ordered reverse chronologically.
    pub next: Vec<SessionHistoryEntry>,
}

impl BrowsingContext {
    /// Create a new browsing context.
    /// Note this just creates the browsing context, it doesn't add it to the constellation's set of browsing contexts.
    pub fn new(id: BrowsingContextId,
               top_level_id: TopLevelBrowsingContextId,
               pipeline_id: PipelineId,
               load_data: LoadData)
               -> BrowsingContext
    {
        BrowsingContext {
            id: id,
            top_level_id: top_level_id,
            size: None,
            pipeline_id: pipeline_id,
            instant: Instant::now(),
            load_data: load_data,
            prev: vec!(),
            next: vec!(),
        }
    }

    /// Get the current session history entry.
    pub fn current(&self) -> SessionHistoryEntry {
        SessionHistoryEntry {
            instant: self.instant,
            browsing_context_id: self.id,
            pipeline_id: Some(self.pipeline_id),
            load_data: self.load_data.clone(),
        }
    }

    /// Set the current session history entry, and push the current frame entry into the past.
    pub fn load(&mut self, pipeline_id: PipelineId, load_data: LoadData) {
        let current = self.current();
        self.prev.push(current);
        self.instant = Instant::now();
        self.pipeline_id = pipeline_id;
        self.load_data = load_data;
    }

    /// Set the future to be empty.
    pub fn remove_forward_entries(&mut self) -> Vec<SessionHistoryEntry> {
        replace(&mut self.next, vec!())
    }

    /// Update the current entry of the BrowsingContext from an entry that has been traversed to.
    pub fn update_current(&mut self, pipeline_id: PipelineId, entry: SessionHistoryEntry) {
        self.pipeline_id = pipeline_id;
        self.instant = entry.instant;
        self.load_data = entry.load_data;
    }
}

/// An entry in a browsing context's session history.
/// Each entry stores the pipeline id for a document in the session history.
///
/// When we operate on the joint session history, entries are sorted chronologically,
/// so we timestamp the entries by when the entry was added to the session history.
///
/// https://html.spec.whatwg.org/multipage/#session-history-entry
#[derive(Clone)]
pub struct SessionHistoryEntry {
    /// The timestamp for when the session history entry was created
    pub instant: Instant,

    /// The pipeline for the document in the session history,
    /// None if the entry has been discarded
    pub pipeline_id: Option<PipelineId>,

    /// The load data for this entry, used to reload the pipeline if it has been discarded
    pub load_data: LoadData,

    /// The frame that this session history entry is part of
    pub browsing_context_id: BrowsingContextId,
}

/// Represents a pending change in a session history, that will be applied
/// once the new pipeline has loaded and completed initial layout / paint.
pub struct SessionHistoryChange {
    /// The browsing context to change.
    pub browsing_context_id: BrowsingContextId,

    /// The top-level browsing context ancestor.
    pub top_level_browsing_context_id: TopLevelBrowsingContextId,

    /// The pipeline for the document being loaded.
    pub new_pipeline_id: PipelineId,

    /// The data for the document being loaded.
    pub load_data: LoadData,

    /// Is the new document replacing the current document (e.g. a reload)
    /// or pushing it into the session history (e.g. a navigation)?
    /// If it is replacing an existing entry, we store its timestamp.
    pub replace_instant: Option<Instant>,
}

/// An iterator over browsing contexts, returning the descendant
/// contexts whose active documents are fully active, in depth-first
/// order.
pub struct FullyActiveBrowsingContextsIterator<'a> {
    /// The browsing contexts still to iterate over.
    pub stack: Vec<BrowsingContextId>,

    /// The set of all browsing contexts.
    pub browsing_contexts: &'a HashMap<BrowsingContextId, BrowsingContext>,

    /// The set of all pipelines.  We use this to find the active
    /// children of a frame, which are the iframes in the currently
    /// active document.
    pub pipelines: &'a HashMap<PipelineId, Pipeline>,
}

impl<'a> Iterator for FullyActiveBrowsingContextsIterator<'a> {
    type Item = &'a BrowsingContext;
    fn next(&mut self) -> Option<&'a BrowsingContext> {
        loop {
            let browsing_context_id = match self.stack.pop() {
                Some(browsing_context_id) => browsing_context_id,
                None => return None,
            };
            let browsing_context = match self.browsing_contexts.get(&browsing_context_id) {
                Some(browsing_context) => browsing_context,
                None => {
                    warn!("BrowsingContext {:?} iterated after closure.", browsing_context_id);
                    continue;
                },
            };
            let pipeline = match self.pipelines.get(&browsing_context.pipeline_id) {
                Some(pipeline) => pipeline,
                None => {
                    warn!("Pipeline {:?} iterated after closure.", browsing_context.pipeline_id);
                    continue;
                },
            };
            self.stack.extend(pipeline.children.iter());
            return Some(browsing_context)
        }
    }
}

/// An iterator over browsing contexts, returning all descendant
/// contexts in depth-first order. Note that this iterator returns all
/// contexts, not just the fully active ones.
pub struct AllBrowsingContextsIterator<'a> {
    /// The browsing contexts still to iterate over.
    pub stack: Vec<BrowsingContextId>,

    /// The set of all browsing contexts.
    pub browsing_contexts: &'a HashMap<BrowsingContextId, BrowsingContext>,

    /// The set of all pipelines.  We use this to find the
    /// children of a browsing context, which are the iframes in all documents
    /// in the session history.
    pub pipelines: &'a HashMap<PipelineId, Pipeline>,
}

impl<'a> Iterator for AllBrowsingContextsIterator<'a> {
    type Item = &'a BrowsingContext;
    fn next(&mut self) -> Option<&'a BrowsingContext> {
        let pipelines = self.pipelines;
        loop {
            let browsing_context_id = match self.stack.pop() {
                Some(browsing_context_id) => browsing_context_id,
                None => return None,
            };
            let browsing_context = match self.browsing_contexts.get(&browsing_context_id) {
                Some(browsing_context) => browsing_context,
                None => {
                    warn!("BrowsingContext {:?} iterated after closure.", browsing_context_id);
                    continue;
                },
            };
            let child_browsing_context_ids = browsing_context.prev.iter().chain(browsing_context.next.iter())
                .filter_map(|entry| entry.pipeline_id)
                .chain(once(browsing_context.pipeline_id))
                .filter_map(|pipeline_id| pipelines.get(&pipeline_id))
                .flat_map(|pipeline| pipeline.children.iter());
            self.stack.extend(child_browsing_context_ids);
            return Some(browsing_context)
        }
    }
}
<commit_msg>Auto merge of #17049 - jklepatch:fix_typo_in_browsingcontext.rs, r=jdm<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 euclid::size::TypedSize2D;
use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, PipelineId};
use pipeline::Pipeline;
use script_traits::LoadData;
use std::collections::HashMap;
use std::iter::once;
use std::mem::replace;
use std::time::Instant;
use style_traits::CSSPixel;

/// The constellation's view of a browsing context.
/// Each browsing context has a session history, caused by
/// navigation and traversing the history. Each browsing context has its
/// current entry, plus past and future entries. The past is sorted
/// chronologically, the future is sorted reverse chronologically:
/// in particular prev.pop() is the latest past entry, and
/// next.pop() is the earliest future entry.
pub struct BrowsingContext {
    /// The browsing context id.
    pub id: BrowsingContextId,

    /// The top-level browsing context ancestor
    pub top_level_id: TopLevelBrowsingContextId,

    /// The size of the frame.
    pub size: Option<TypedSize2D<f32, CSSPixel>>,

    /// The timestamp for the current session history entry.
    pub instant: Instant,

    /// The pipeline for the current session history entry.
    pub pipeline_id: PipelineId,

    /// The load data for the current session history entry.
    pub load_data: LoadData,

    /// The past session history, ordered chronologically.
    pub prev: Vec<SessionHistoryEntry>,

    /// The future session history, ordered reverse chronologically.
    pub next: Vec<SessionHistoryEntry>,
}

impl BrowsingContext {
    /// Create a new browsing context.
    /// Note this just creates the browsing context, it doesn't add it to the constellation's set of browsing contexts.
    pub fn new(id: BrowsingContextId,
               top_level_id: TopLevelBrowsingContextId,
               pipeline_id: PipelineId,
               load_data: LoadData)
               -> BrowsingContext
    {
        BrowsingContext {
            id: id,
            top_level_id: top_level_id,
            size: None,
            pipeline_id: pipeline_id,
            instant: Instant::now(),
            load_data: load_data,
            prev: vec!(),
            next: vec!(),
        }
    }

    /// Get the current session history entry.
    pub fn current(&self) -> SessionHistoryEntry {
        SessionHistoryEntry {
            instant: self.instant,
            browsing_context_id: self.id,
            pipeline_id: Some(self.pipeline_id),
            load_data: self.load_data.clone(),
        }
    }

    /// Set the current session history entry, and push the current frame entry into the past.
    pub fn load(&mut self, pipeline_id: PipelineId, load_data: LoadData) {
        let current = self.current();
        self.prev.push(current);
        self.instant = Instant::now();
        self.pipeline_id = pipeline_id;
        self.load_data = load_data;
    }

    /// Set the future to be empty.
    pub fn remove_forward_entries(&mut self) -> Vec<SessionHistoryEntry> {
        replace(&mut self.next, vec!())
    }

    /// Update the current entry of the BrowsingContext from an entry that has been traversed to.
    pub fn update_current(&mut self, pipeline_id: PipelineId, entry: SessionHistoryEntry) {
        self.pipeline_id = pipeline_id;
        self.instant = entry.instant;
        self.load_data = entry.load_data;
    }
}

/// An entry in a browsing context's session history.
/// Each entry stores the pipeline id for a document in the session history.
///
/// When we operate on the joint session history, entries are sorted chronologically,
/// so we timestamp the entries by when the entry was added to the session history.
///
/// https://html.spec.whatwg.org/multipage/#session-history-entry
#[derive(Clone)]
pub struct SessionHistoryEntry {
    /// The timestamp for when the session history entry was created
    pub instant: Instant,

    /// The pipeline for the document in the session history,
    /// None if the entry has been discarded
    pub pipeline_id: Option<PipelineId>,

    /// The load data for this entry, used to reload the pipeline if it has been discarded
    pub load_data: LoadData,

    /// The frame that this session history entry is part of
    pub browsing_context_id: BrowsingContextId,
}

/// Represents a pending change in a session history, that will be applied
/// once the new pipeline has loaded and completed initial layout / paint.
pub struct SessionHistoryChange {
    /// The browsing context to change.
    pub browsing_context_id: BrowsingContextId,

    /// The top-level browsing context ancestor.
    pub top_level_browsing_context_id: TopLevelBrowsingContextId,

    /// The pipeline for the document being loaded.
    pub new_pipeline_id: PipelineId,

    /// The data for the document being loaded.
    pub load_data: LoadData,

    /// Is the new document replacing the current document (e.g. a reload)
    /// or pushing it into the session history (e.g. a navigation)?
    /// If it is replacing an existing entry, we store its timestamp.
    pub replace_instant: Option<Instant>,
}

/// An iterator over browsing contexts, returning the descendant
/// contexts whose active documents are fully active, in depth-first
/// order.
pub struct FullyActiveBrowsingContextsIterator<'a> {
    /// The browsing contexts still to iterate over.
    pub stack: Vec<BrowsingContextId>,

    /// The set of all browsing contexts.
    pub browsing_contexts: &'a HashMap<BrowsingContextId, BrowsingContext>,

    /// The set of all pipelines.  We use this to find the active
    /// children of a frame, which are the iframes in the currently
    /// active document.
    pub pipelines: &'a HashMap<PipelineId, Pipeline>,
}

impl<'a> Iterator for FullyActiveBrowsingContextsIterator<'a> {
    type Item = &'a BrowsingContext;
    fn next(&mut self) -> Option<&'a BrowsingContext> {
        loop {
            let browsing_context_id = match self.stack.pop() {
                Some(browsing_context_id) => browsing_context_id,
                None => return None,
            };
            let browsing_context = match self.browsing_contexts.get(&browsing_context_id) {
                Some(browsing_context) => browsing_context,
                None => {
                    warn!("BrowsingContext {:?} iterated after closure.", browsing_context_id);
                    continue;
                },
            };
            let pipeline = match self.pipelines.get(&browsing_context.pipeline_id) {
                Some(pipeline) => pipeline,
                None => {
                    warn!("Pipeline {:?} iterated after closure.", browsing_context.pipeline_id);
                    continue;
                },
            };
            self.stack.extend(pipeline.children.iter());
            return Some(browsing_context)
        }
    }
}

/// An iterator over browsing contexts, returning all descendant
/// contexts in depth-first order. Note that this iterator returns all
/// contexts, not just the fully active ones.
pub struct AllBrowsingContextsIterator<'a> {
    /// The browsing contexts still to iterate over.
    pub stack: Vec<BrowsingContextId>,

    /// The set of all browsing contexts.
    pub browsing_contexts: &'a HashMap<BrowsingContextId, BrowsingContext>,

    /// The set of all pipelines.  We use this to find the
    /// children of a browsing context, which are the iframes in all documents
    /// in the session history.
    pub pipelines: &'a HashMap<PipelineId, Pipeline>,
}

impl<'a> Iterator for AllBrowsingContextsIterator<'a> {
    type Item = &'a BrowsingContext;
    fn next(&mut self) -> Option<&'a BrowsingContext> {
        let pipelines = self.pipelines;
        loop {
            let browsing_context_id = match self.stack.pop() {
                Some(browsing_context_id) => browsing_context_id,
                None => return None,
            };
            let browsing_context = match self.browsing_contexts.get(&browsing_context_id) {
                Some(browsing_context) => browsing_context,
                None => {
                    warn!("BrowsingContext {:?} iterated after closure.", browsing_context_id);
                    continue;
                },
            };
            let child_browsing_context_ids = browsing_context.prev.iter().chain(browsing_context.next.iter())
                .filter_map(|entry| entry.pipeline_id)
                .chain(once(browsing_context.pipeline_id))
                .filter_map(|pipeline_id| pipelines.get(&pipeline_id))
                .flat_map(|pipeline| pipeline.children.iter());
            self.stack.extend(child_browsing_context_ids);
            return Some(browsing_context)
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

    use std::env;
    use std::ffi::{OsStr, OsString};
    use std::path::{Path, PathBuf};
    use std::process::{Command, Stdio};

    use common::ui::UI;
    use hcore::crypto::{default_cache_key_path};
    use hcore::env as henv;
    use hcore::fs::{CACHE_ARTIFACT_PATH, CACHE_KEY_PATH, find_command};
    use hcore::os::process;
    use command::studio::enter::ARTIFACT_PATH_ENVVAR;

    use error::{Error, Result};
    use VERSION;

    const DOCKER_CMD: &'static str = "docker";
    const DOCKER_CMD_ENVVAR: &'static str = "HAB_DOCKER_BINARY";
    const DOCKER_IMAGE: &'static str = "habitat-docker-registry.bintray.io/studio";
    const DOCKER_IMAGE_ENVVAR: &'static str = "HAB_DOCKER_STUDIO_IMAGE";
    const DOCKER_OPTS_ENVVAR: &'static str = "HAB_DOCKER_OPTS";
    const DOCKER_SOCKET: &'static str = "/var/run/docker.sock";

    pub fn start_docker_studio(_ui: &mut UI, mut args: Vec<OsString>) -> Result<()> {
        let docker_cmd = find_docker_cmd()?;

        if is_image_present(&docker_cmd) {
            debug!("Found Studio Docker image locally.");
        } else {
            debug!("Failed to find Studio Docker image locally.");
            pull_image(&docker_cmd)?;
        }

        let mut volumes = vec![
            format!("{}:/src", env::current_dir().unwrap().to_string_lossy()),
            format!(
                "{}:/{}",
                default_cache_key_path(None).to_string_lossy(),
                CACHE_KEY_PATH
            ),
        ];
        if let Ok(cache_artifact_path) = henv::var(ARTIFACT_PATH_ENVVAR) {
            volumes.push(format!("{}:/{}", cache_artifact_path, CACHE_ARTIFACT_PATH));
        }
        if Path::new(DOCKER_SOCKET).exists() || cfg!(target_os = "windows") {
            volumes.push(format!("{}:{}", DOCKER_SOCKET, DOCKER_SOCKET));
        }

        let env_vars = vec![
            "HAB_DEPOT_URL",
            "HAB_DEPOT_CHANNEL",
            "HAB_ORIGIN",
            "HAB_STUDIO_SUP",
            "HAB_UPDATE_STRATEGY_FREQUENCY_MS",
            "http_proxy",
            "https_proxy",
        ];
        // We need to strip out the -D if it exists to avoid
        // it getting passed to the sup on entering the studio
        let to_cull = OsString::from("-D");
        let index = args.iter().position(|x| *x == to_cull).unwrap();
        args.remove(index);

        check_mounts(&docker_cmd, volumes.iter())?;
        run_container(docker_cmd, args, volumes.iter(), env_vars.iter())
    }

    fn find_docker_cmd() -> Result<PathBuf> {
        let docker_cmd = henv::var(DOCKER_CMD_ENVVAR).unwrap_or(DOCKER_CMD.to_string());

        match find_command(&docker_cmd) {
            Some(docker_abs_path) => Ok(docker_abs_path),
            None => Err(Error::ExecCommandNotFound(docker_cmd.into())),
        }
    }

    fn is_image_present(docker_cmd: &Path) -> bool {
        let mut cmd = Command::new(docker_cmd);
        cmd.arg("images").arg(&image_identifier()).arg("-q");
        debug!("Running command: {:?}", cmd);
        let result = cmd.output().expect("Docker command failed to spawn");

        !String::from_utf8_lossy(&result.stdout).as_ref().is_empty()
    }

    fn pull_image(docker_cmd: &Path) -> Result<()> {
        let image = image_identifier();
        let mut cmd = Command::new(docker_cmd);
        cmd.arg("pull")
            .arg(&image)
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit());
        debug!("Running command: {:?}", cmd);
        let result = cmd.spawn()
            .expect("Docker command failed to spawn")
            .wait_with_output()
            .expect("Failed to wait on child process");

        if result.status.success() {
            debug!("Docker image '{}' is present locally.", &image);
        } else {
            debug!(
                "Pulling Docker image '{}' failed with exit code: {:?}",
                &image,
                result.status
            );

            let err_output = String::from_utf8_lossy(&result.stderr);

            if err_output.contains("image") && err_output.contains("not found") {
                return Err(Error::DockerImageNotFound(image_identifier().to_string()));
            } else if err_output.contains("Cannot connect to the Docker daemon") {
                return Err(Error::DockerDaemonDown);
            } else {
                return Err(Error::DockerNetworkDown(image_identifier().to_string()));
            }
        }

        Ok(())
    }

    /// Checks whether or not the volume mounts are working.
    ///
    /// We need to ensure that filesystem sharing has been enabled, otherwise the user will be
    /// greeted with a horrible error message that's difficult to make sense of. To mitigate this,
    /// we check the studio version. This will cause Docker to go through the mounting steps, so we
    /// can watch stderr for failure, but has the advantage of not requiring a TTY.
    fn check_mounts<I, S>(docker_cmd: &Path, volumes: I) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        let mut volume_args: Vec<OsString> = Vec::new();
        for vol in volumes {
            volume_args.push("--volume".into());
            volume_args.push(vol.as_ref().into());
        }

        let version_output = Command::new(docker_cmd)
            .arg("run")
            .arg("--rm")
            .arg("--privileged")
            .args(volume_args)
            .arg(image_identifier())
            .arg("-V")
            .output()
            .expect("docker failed to start");

        let stderr = String::from_utf8(version_output.stderr).unwrap();
        if !stderr.is_empty() &&
            (stderr.as_str().contains("Mounts denied") ||
                 stderr.as_str().contains("drive is not shared"))
        {
            return Err(Error::DockerFileSharingNotEnabled);
        }
        Ok(())
    }

    fn run_container<I, J, S, T>(
        docker_cmd: PathBuf,
        args: Vec<OsString>,
        volumes: I,
        env_vars: J,
    ) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        J: IntoIterator<Item = T>,
        S: AsRef<OsStr>,
        T: AsRef<str>,
    {
        let mut cmd_args: Vec<OsString> = vec![
            "run".into(),
            "--rm".into(),
            "--tty".into(),
            "--interactive".into(),
            "--privileged".into(),
        ];
        if let Ok(opts) = henv::var(DOCKER_OPTS_ENVVAR) {
            let opts = opts.split(" ")
                .map(|v| v.into())
                // Ensure we're not passing something like `--tty` again here.
                .filter(|v| !cmd_args.contains(v))
                .collect::<Vec<_>>();
            if !opts.is_empty() {
                debug!(
                    "Adding extra Docker options from {} = {:?}",
                    DOCKER_OPTS_ENVVAR,
                    opts
                );
                cmd_args.extend_from_slice(opts.as_slice());
            }
        }
        for var in env_vars {
            if let Ok(val) = henv::var(var.as_ref()) {
                debug!("Setting container env var: {:?}='{}'", var.as_ref(), val);
                cmd_args.push("--env".into());
                cmd_args.push(format!("{}={}", var.as_ref(), val).into());
            }
        }
        for vol in volumes {
            cmd_args.push("--volume".into());
            cmd_args.push(vol.as_ref().into());
        }
        cmd_args.push(image_identifier().into());
        cmd_args.extend_from_slice(args.as_slice());

        unset_proxy_env_vars();
        Ok(process::become_command(docker_cmd, cmd_args)?)
    }

    fn unset_proxy_env_vars() {
        for var in vec!["http_proxy", "https_proxy"] {
            if let Ok(_) = henv::var(var) {
                debug!("Unsetting process environment variable '{}'", var);
                env::remove_var(var);
            }
        }
    }

    /// Returns the Docker Studio image with tag for the desired version which corresponds to the
    /// same version (minus release) as this program.
    fn image_identifier() -> String {
        let version: Vec<&str> = VERSION.split("/").collect();
        henv::var(DOCKER_IMAGE_ENVVAR).unwrap_or(format!("{}:{}", DOCKER_IMAGE, version[0]))
    }
<commit_msg>fix docker studio panic on mac/windows when no args passed<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

    use std::env;
    use std::ffi::{OsStr, OsString};
    use std::path::{Path, PathBuf};
    use std::process::{Command, Stdio};

    use common::ui::UI;
    use hcore::crypto::{default_cache_key_path};
    use hcore::env as henv;
    use hcore::fs::{CACHE_ARTIFACT_PATH, CACHE_KEY_PATH, find_command};
    use hcore::os::process;
    use command::studio::enter::ARTIFACT_PATH_ENVVAR;

    use error::{Error, Result};
    use VERSION;

    const DOCKER_CMD: &'static str = "docker";
    const DOCKER_CMD_ENVVAR: &'static str = "HAB_DOCKER_BINARY";
    const DOCKER_IMAGE: &'static str = "habitat-docker-registry.bintray.io/studio";
    const DOCKER_IMAGE_ENVVAR: &'static str = "HAB_DOCKER_STUDIO_IMAGE";
    const DOCKER_OPTS_ENVVAR: &'static str = "HAB_DOCKER_OPTS";
    const DOCKER_SOCKET: &'static str = "/var/run/docker.sock";

    pub fn start_docker_studio(_ui: &mut UI, mut args: Vec<OsString>) -> Result<()> {
        let docker_cmd = find_docker_cmd()?;

        if is_image_present(&docker_cmd) {
            debug!("Found Studio Docker image locally.");
        } else {
            debug!("Failed to find Studio Docker image locally.");
            pull_image(&docker_cmd)?;
        }

        let mut volumes = vec![
            format!("{}:/src", env::current_dir().unwrap().to_string_lossy()),
            format!(
                "{}:/{}",
                default_cache_key_path(None).to_string_lossy(),
                CACHE_KEY_PATH
            ),
        ];
        if let Ok(cache_artifact_path) = henv::var(ARTIFACT_PATH_ENVVAR) {
            volumes.push(format!("{}:/{}", cache_artifact_path, CACHE_ARTIFACT_PATH));
        }
        if Path::new(DOCKER_SOCKET).exists() || cfg!(target_os = "windows") {
            volumes.push(format!("{}:{}", DOCKER_SOCKET, DOCKER_SOCKET));
        }

        let env_vars = vec![
            "HAB_DEPOT_URL",
            "HAB_DEPOT_CHANNEL",
            "HAB_ORIGIN",
            "HAB_STUDIO_SUP",
            "HAB_UPDATE_STRATEGY_FREQUENCY_MS",
            "http_proxy",
            "https_proxy",
        ];
        // We need to strip out the -D if it exists to avoid
        // it getting passed to the sup on entering the studio
        let to_cull = OsString::from("-D");
        if let Some(index) = args.iter().position(|x| *x == to_cull) {
            args.remove(index);
        }

        check_mounts(&docker_cmd, volumes.iter())?;
        run_container(docker_cmd, args, volumes.iter(), env_vars.iter())
    }

    fn find_docker_cmd() -> Result<PathBuf> {
        let docker_cmd = henv::var(DOCKER_CMD_ENVVAR).unwrap_or(DOCKER_CMD.to_string());

        match find_command(&docker_cmd) {
            Some(docker_abs_path) => Ok(docker_abs_path),
            None => Err(Error::ExecCommandNotFound(docker_cmd.into())),
        }
    }

    fn is_image_present(docker_cmd: &Path) -> bool {
        let mut cmd = Command::new(docker_cmd);
        cmd.arg("images").arg(&image_identifier()).arg("-q");
        debug!("Running command: {:?}", cmd);
        let result = cmd.output().expect("Docker command failed to spawn");

        !String::from_utf8_lossy(&result.stdout).as_ref().is_empty()
    }

    fn pull_image(docker_cmd: &Path) -> Result<()> {
        let image = image_identifier();
        let mut cmd = Command::new(docker_cmd);
        cmd.arg("pull")
            .arg(&image)
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit());
        debug!("Running command: {:?}", cmd);
        let result = cmd.spawn()
            .expect("Docker command failed to spawn")
            .wait_with_output()
            .expect("Failed to wait on child process");

        if result.status.success() {
            debug!("Docker image '{}' is present locally.", &image);
        } else {
            debug!(
                "Pulling Docker image '{}' failed with exit code: {:?}",
                &image,
                result.status
            );

            let err_output = String::from_utf8_lossy(&result.stderr);

            if err_output.contains("image") && err_output.contains("not found") {
                return Err(Error::DockerImageNotFound(image_identifier().to_string()));
            } else if err_output.contains("Cannot connect to the Docker daemon") {
                return Err(Error::DockerDaemonDown);
            } else {
                return Err(Error::DockerNetworkDown(image_identifier().to_string()));
            }
        }

        Ok(())
    }

    /// Checks whether or not the volume mounts are working.
    ///
    /// We need to ensure that filesystem sharing has been enabled, otherwise the user will be
    /// greeted with a horrible error message that's difficult to make sense of. To mitigate this,
    /// we check the studio version. This will cause Docker to go through the mounting steps, so we
    /// can watch stderr for failure, but has the advantage of not requiring a TTY.
    fn check_mounts<I, S>(docker_cmd: &Path, volumes: I) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        let mut volume_args: Vec<OsString> = Vec::new();
        for vol in volumes {
            volume_args.push("--volume".into());
            volume_args.push(vol.as_ref().into());
        }

        let version_output = Command::new(docker_cmd)
            .arg("run")
            .arg("--rm")
            .arg("--privileged")
            .args(volume_args)
            .arg(image_identifier())
            .arg("-V")
            .output()
            .expect("docker failed to start");

        let stderr = String::from_utf8(version_output.stderr).unwrap();
        if !stderr.is_empty() &&
            (stderr.as_str().contains("Mounts denied") ||
                 stderr.as_str().contains("drive is not shared"))
        {
            return Err(Error::DockerFileSharingNotEnabled);
        }
        Ok(())
    }

    fn run_container<I, J, S, T>(
        docker_cmd: PathBuf,
        args: Vec<OsString>,
        volumes: I,
        env_vars: J,
    ) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        J: IntoIterator<Item = T>,
        S: AsRef<OsStr>,
        T: AsRef<str>,
    {
        let mut cmd_args: Vec<OsString> = vec![
            "run".into(),
            "--rm".into(),
            "--tty".into(),
            "--interactive".into(),
            "--privileged".into(),
        ];
        if let Ok(opts) = henv::var(DOCKER_OPTS_ENVVAR) {
            let opts = opts.split(" ")
                .map(|v| v.into())
                // Ensure we're not passing something like `--tty` again here.
                .filter(|v| !cmd_args.contains(v))
                .collect::<Vec<_>>();
            if !opts.is_empty() {
                debug!(
                    "Adding extra Docker options from {} = {:?}",
                    DOCKER_OPTS_ENVVAR,
                    opts
                );
                cmd_args.extend_from_slice(opts.as_slice());
            }
        }
        for var in env_vars {
            if let Ok(val) = henv::var(var.as_ref()) {
                debug!("Setting container env var: {:?}='{}'", var.as_ref(), val);
                cmd_args.push("--env".into());
                cmd_args.push(format!("{}={}", var.as_ref(), val).into());
            }
        }
        for vol in volumes {
            cmd_args.push("--volume".into());
            cmd_args.push(vol.as_ref().into());
        }
        cmd_args.push(image_identifier().into());
        cmd_args.extend_from_slice(args.as_slice());

        unset_proxy_env_vars();
        Ok(process::become_command(docker_cmd, cmd_args)?)
    }

    fn unset_proxy_env_vars() {
        for var in vec!["http_proxy", "https_proxy"] {
            if let Ok(_) = henv::var(var) {
                debug!("Unsetting process environment variable '{}'", var);
                env::remove_var(var);
            }
        }
    }

    /// Returns the Docker Studio image with tag for the desired version which corresponds to the
    /// same version (minus release) as this program.
    fn image_identifier() -> String {
        let version: Vec<&str> = VERSION.split("/").collect();
        henv::var(DOCKER_IMAGE_ENVVAR).unwrap_or(format!("{}:{}", DOCKER_IMAGE, version[0]))
    }
<|endoftext|>


<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/. */

//! Animated types for CSS values related to effects.

use properties::animated_properties::Animatable;
use properties::longhands::box_shadow::computed_value::T as ComputedBoxShadowList;
use properties::longhands::filter::computed_value::T as ComputedFilterList;
use properties::longhands::text_shadow::computed_value::T as ComputedTextShadowList;
use std::cmp;
#[cfg(not(feature = "gecko"))]
use values::Impossible;
use values::animated::{ToAnimatedValue, ToAnimatedZero};
use values::animated::color::Color;
use values::computed::{Angle, NonNegativeNumber};
use values::computed::length::{Length, NonNegativeLength};
use values::distance::{ComputeSquaredDistance, SquaredDistance};
use values::generics::effects::BoxShadow as GenericBoxShadow;
use values::generics::effects::Filter as GenericFilter;
use values::generics::effects::SimpleShadow as GenericSimpleShadow;

/// An animated value for the `box-shadow` property.
pub type BoxShadowList = ShadowList<BoxShadow>;

/// An animated value for the `text-shadow` property.
pub type TextShadowList = ShadowList<SimpleShadow>;

/// An animated value for shadow lists.
///
/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq)]
pub struct ShadowList<Shadow>(Vec<Shadow>);

/// An animated value for a single `box-shadow`.
pub type BoxShadow = GenericBoxShadow<Color, Length, NonNegativeLength, Length>;

/// An animated value for the `filter` property.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq)]
pub struct FilterList(pub Vec<Filter>);

/// An animated value for a single `filter`.
#[cfg(feature = "gecko")]
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, SimpleShadow>;

/// An animated value for a single `filter`.
#[cfg(not(feature = "gecko"))]
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible>;

/// An animated value for the `drop-shadow()` filter.
pub type SimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>;

impl ToAnimatedValue for ComputedBoxShadowList {
    type AnimatedValue = BoxShadowList;

    #[inline]
    fn to_animated_value(self) -> Self::AnimatedValue {
        ShadowList(self.0.to_animated_value())
    }

    #[inline]
    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
        ComputedBoxShadowList(ToAnimatedValue::from_animated_value(animated.0))
    }
}

impl<S> Animatable for ShadowList<S>
where
    S: Animatable + Clone + ToAnimatedZero,
{
    #[inline]
    fn add_weighted(
        &self,
        other: &Self,
        self_portion: f64,
        other_portion: f64,
    ) -> Result<Self, ()> {
        let max_len = cmp::max(self.0.len(), other.0.len());
        let mut shadows = Vec::with_capacity(max_len);
        for i in 0..max_len {
            shadows.push(match (self.0.get(i), other.0.get(i)) {
                (Some(shadow), Some(other)) => {
                    shadow.add_weighted(other, self_portion, other_portion)?
                },
                (Some(shadow), None) => {
                    shadow.add_weighted(&shadow.to_animated_zero()?, self_portion, other_portion)?
                },
                (None, Some(shadow)) => {
                    shadow.to_animated_zero()?.add_weighted(&shadow, self_portion, other_portion)?
                },
                (None, None) => unreachable!(),
            });
        }
        Ok(ShadowList(shadows))
    }

    #[inline]
    fn add(&self, other: &Self) -> Result<Self, ()> {
        Ok(ShadowList(
            self.0.iter().cloned().chain(other.0.iter().cloned()).collect(),
        ))
    }
}

impl<S> ComputeSquaredDistance for ShadowList<S> {
    #[inline]
    fn compute_squared_distance(&self, _other: &Self) -> Result<SquaredDistance, ()> {
        // FIXME: This should be implemented.
        Err(())
    }
}

impl<S> ToAnimatedZero for ShadowList<S> {
    #[inline]
    fn to_animated_zero(&self) -> Result<Self, ()> {
        Ok(ShadowList(vec![]))
    }
}

impl ToAnimatedValue for ComputedTextShadowList {
    type AnimatedValue = TextShadowList;

    #[inline]
    fn to_animated_value(self) -> Self::AnimatedValue {
        ShadowList(self.0.to_animated_value())
    }

    #[inline]
    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
        ComputedTextShadowList(ToAnimatedValue::from_animated_value(animated.0))
    }
}

impl Animatable for BoxShadow {
    #[inline]
    fn add_weighted(
        &self,
        other: &Self,
        self_portion: f64,
        other_portion: f64,
    ) -> Result<Self, ()> {
        if self.inset != other.inset {
            return Err(());
        }
        Ok(BoxShadow {
            base: self.base.add_weighted(&other.base, self_portion, other_portion)?,
            spread: self.spread.add_weighted(&other.spread, self_portion, other_portion)?,
            inset: self.inset,
        })
    }
}

impl ComputeSquaredDistance for BoxShadow {
    #[inline]
    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
        if self.inset != other.inset {
            return Err(());
        }
        Ok(
            self.base.compute_squared_distance(&other.base)? +
            self.spread.compute_squared_distance(&other.spread)?,
        )
    }
}

impl ToAnimatedZero for BoxShadow {
    #[inline]
    fn to_animated_zero(&self) -> Result<Self, ()> {
        Ok(BoxShadow {
            base: self.base.to_animated_zero()?,
            spread: self.spread.to_animated_zero()?,
            inset: self.inset,
        })
    }
}

impl ToAnimatedValue for ComputedFilterList {
    type AnimatedValue = FilterList;

    #[cfg(not(feature = "gecko"))]
    #[inline]
    fn to_animated_value(self) -> Self::AnimatedValue {
        FilterList(self.0)
    }

    #[cfg(feature = "gecko")]
    #[inline]
    fn to_animated_value(self) -> Self::AnimatedValue {
        FilterList(self.0.to_animated_value())
    }

    #[cfg(not(feature = "gecko"))]
    #[inline]
    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
        ComputedFilterList(animated.0)
    }

    #[cfg(feature = "gecko")]
    #[inline]
    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
        ComputedFilterList(ToAnimatedValue::from_animated_value(animated.0))
    }
}

impl ToAnimatedZero for FilterList {
    #[inline]
    fn to_animated_zero(&self) -> Result<Self, ()> {
        Ok(FilterList(vec![]))
    }
}

impl Animatable for SimpleShadow {
    #[inline]
    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
        let color = self.color.add_weighted(&other.color, self_portion, other_portion)?;
        let horizontal = self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?;
        let vertical = self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?;
        let blur = self.blur.add_weighted(&other.blur, self_portion, other_portion)?;

        Ok(SimpleShadow {
            color: color,
            horizontal: horizontal,
            vertical: vertical,
            blur: blur,
        })
    }
}

impl ToAnimatedZero for SimpleShadow {
    #[inline]
    fn to_animated_zero(&self) -> Result<Self, ()> {
        Ok(SimpleShadow {
            color: Color::transparent(),
            horizontal: self.horizontal.to_animated_zero()?,
            vertical: self.vertical.to_animated_zero()?,
            blur: self.blur.to_animated_zero()?,
        })
    }
}
<commit_msg>Auto merge of #18102 - hiikezoe:compute-distance-for-shadow-list, r=boris<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/. */

//! Animated types for CSS values related to effects.

use properties::animated_properties::Animatable;
use properties::longhands::box_shadow::computed_value::T as ComputedBoxShadowList;
use properties::longhands::filter::computed_value::T as ComputedFilterList;
use properties::longhands::text_shadow::computed_value::T as ComputedTextShadowList;
use std::cmp;
#[cfg(not(feature = "gecko"))]
use values::Impossible;
use values::animated::{ToAnimatedValue, ToAnimatedZero};
use values::animated::color::Color;
use values::computed::{Angle, NonNegativeNumber};
use values::computed::length::{Length, NonNegativeLength};
use values::distance::{ComputeSquaredDistance, SquaredDistance};
use values::generics::effects::BoxShadow as GenericBoxShadow;
use values::generics::effects::Filter as GenericFilter;
use values::generics::effects::SimpleShadow as GenericSimpleShadow;

/// An animated value for the `box-shadow` property.
pub type BoxShadowList = ShadowList<BoxShadow>;

/// An animated value for the `text-shadow` property.
pub type TextShadowList = ShadowList<SimpleShadow>;

/// An animated value for shadow lists.
///
/// https://drafts.csswg.org/css-transitions/#animtype-shadow-list
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq)]
pub struct ShadowList<Shadow>(Vec<Shadow>);

/// An animated value for a single `box-shadow`.
pub type BoxShadow = GenericBoxShadow<Color, Length, NonNegativeLength, Length>;

/// An animated value for the `filter` property.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Debug, PartialEq)]
pub struct FilterList(pub Vec<Filter>);

/// An animated value for a single `filter`.
#[cfg(feature = "gecko")]
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, SimpleShadow>;

/// An animated value for a single `filter`.
#[cfg(not(feature = "gecko"))]
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible>;

/// An animated value for the `drop-shadow()` filter.
pub type SimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>;

impl ToAnimatedValue for ComputedBoxShadowList {
    type AnimatedValue = BoxShadowList;

    #[inline]
    fn to_animated_value(self) -> Self::AnimatedValue {
        ShadowList(self.0.to_animated_value())
    }

    #[inline]
    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
        ComputedBoxShadowList(ToAnimatedValue::from_animated_value(animated.0))
    }
}

impl<S> Animatable for ShadowList<S>
where
    S: Animatable + Clone + ToAnimatedZero,
{
    #[inline]
    fn add_weighted(
        &self,
        other: &Self,
        self_portion: f64,
        other_portion: f64,
    ) -> Result<Self, ()> {
        let max_len = cmp::max(self.0.len(), other.0.len());
        let mut shadows = Vec::with_capacity(max_len);
        for i in 0..max_len {
            shadows.push(match (self.0.get(i), other.0.get(i)) {
                (Some(shadow), Some(other)) => {
                    shadow.add_weighted(other, self_portion, other_portion)?
                },
                (Some(shadow), None) => {
                    shadow.add_weighted(&shadow.to_animated_zero()?, self_portion, other_portion)?
                },
                (None, Some(shadow)) => {
                    shadow.to_animated_zero()?.add_weighted(&shadow, self_portion, other_portion)?
                },
                (None, None) => unreachable!(),
            });
        }
        Ok(ShadowList(shadows))
    }

    #[inline]
    fn add(&self, other: &Self) -> Result<Self, ()> {
        Ok(ShadowList(
            self.0.iter().cloned().chain(other.0.iter().cloned()).collect(),
        ))
    }
}

impl<S> ComputeSquaredDistance for ShadowList<S>
where
    S: ComputeSquaredDistance + ToAnimatedZero,
{
    #[inline]
    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
        use itertools::{EitherOrBoth, Itertools};

        self.0.iter().zip_longest(other.0.iter()).map(|it| {
            match it {
                EitherOrBoth::Both(from, to) => {
                    from.compute_squared_distance(to)
                },
                EitherOrBoth::Left(list) | EitherOrBoth::Right(list) => {
                    list.compute_squared_distance(&list.to_animated_zero()?)
                },
            }
        }).sum()
    }
}

impl<S> ToAnimatedZero for ShadowList<S> {
    #[inline]
    fn to_animated_zero(&self) -> Result<Self, ()> {
        Ok(ShadowList(vec![]))
    }
}

impl ToAnimatedValue for ComputedTextShadowList {
    type AnimatedValue = TextShadowList;

    #[inline]
    fn to_animated_value(self) -> Self::AnimatedValue {
        ShadowList(self.0.to_animated_value())
    }

    #[inline]
    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
        ComputedTextShadowList(ToAnimatedValue::from_animated_value(animated.0))
    }
}

impl Animatable for BoxShadow {
    #[inline]
    fn add_weighted(
        &self,
        other: &Self,
        self_portion: f64,
        other_portion: f64,
    ) -> Result<Self, ()> {
        if self.inset != other.inset {
            return Err(());
        }
        Ok(BoxShadow {
            base: self.base.add_weighted(&other.base, self_portion, other_portion)?,
            spread: self.spread.add_weighted(&other.spread, self_portion, other_portion)?,
            inset: self.inset,
        })
    }
}

impl ComputeSquaredDistance for BoxShadow {
    #[inline]
    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
        if self.inset != other.inset {
            return Err(());
        }
        Ok(
            self.base.compute_squared_distance(&other.base)? +
            self.spread.compute_squared_distance(&other.spread)?,
        )
    }
}

impl ToAnimatedZero for BoxShadow {
    #[inline]
    fn to_animated_zero(&self) -> Result<Self, ()> {
        Ok(BoxShadow {
            base: self.base.to_animated_zero()?,
            spread: self.spread.to_animated_zero()?,
            inset: self.inset,
        })
    }
}

impl ToAnimatedValue for ComputedFilterList {
    type AnimatedValue = FilterList;

    #[cfg(not(feature = "gecko"))]
    #[inline]
    fn to_animated_value(self) -> Self::AnimatedValue {
        FilterList(self.0)
    }

    #[cfg(feature = "gecko")]
    #[inline]
    fn to_animated_value(self) -> Self::AnimatedValue {
        FilterList(self.0.to_animated_value())
    }

    #[cfg(not(feature = "gecko"))]
    #[inline]
    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
        ComputedFilterList(animated.0)
    }

    #[cfg(feature = "gecko")]
    #[inline]
    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
        ComputedFilterList(ToAnimatedValue::from_animated_value(animated.0))
    }
}

impl ToAnimatedZero for FilterList {
    #[inline]
    fn to_animated_zero(&self) -> Result<Self, ()> {
        Ok(FilterList(vec![]))
    }
}

impl Animatable for SimpleShadow {
    #[inline]
    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
        let color = self.color.add_weighted(&other.color, self_portion, other_portion)?;
        let horizontal = self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?;
        let vertical = self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?;
        let blur = self.blur.add_weighted(&other.blur, self_portion, other_portion)?;

        Ok(SimpleShadow {
            color: color,
            horizontal: horizontal,
            vertical: vertical,
            blur: blur,
        })
    }
}

impl ToAnimatedZero for SimpleShadow {
    #[inline]
    fn to_animated_zero(&self) -> Result<Self, ()> {
        Ok(SimpleShadow {
            color: Color::transparent(),
            horizontal: self.horizontal.to_animated_zero()?,
            vertical: self.vertical.to_animated_zero()?,
            blur: self.blur.to_animated_zero()?,
        })
    }
}
<|endoftext|>


<commit_before>use raytracer::animator::CameraKeyframe;
use raytracer::Renderer;
use scene::{Camera, Scene};
use std::sync::Arc;
use vec3::Vec3;

pub struct Animator {
    pub fps: f64,
    pub length: f64, // rounded down to nearest frame
    pub renderer: Renderer
}

// TODO: Non-linear interpolation
// TODO: Improve keyframes (sort/order them as we don't need dynamic keyframe insertion)
impl Animator {
    // TODO: make this a Surface iterator so both single frame and animation
    // process flows are similar
    pub fn animate(&self, camera: Camera, shared_scene: Arc<Scene>, filename: &str) {
        let start_time = ::time::get_time();
        let total_frames = (self.fps * self.length).floor() as uint;

        for frame_number in range(0, total_frames) {
            let time = frame_number as f64 / self.fps;
            let lerped_camera = Animator::lerp_camera(&camera, time);
            let frame_data = self.renderer.render(lerped_camera, shared_scene.clone());

            ::util::export::to_ppm(frame_data, format!("{}{:06u}.ppm", filename, frame_number).as_slice());
            ::util::print_progress("*** Frame", start_time, frame_number + 1 as uint, total_frames);
            println!("");
        }
    }

    fn get_neighbour_keyframes(keyframes: Vec<CameraKeyframe>, time: f64)
                               -> (CameraKeyframe, CameraKeyframe, f64) {

        if keyframes.len() <= 1 {
            fail!("Not enough keyframes to interpolate: got: {} expected: >= 2", keyframes.len());
        }

        // Get the two keyframes inbetween current time
        let mut first = &keyframes[0];
        let mut second = &keyframes[1];

        for keyframe in keyframes.iter() {
            if keyframe.time <= time && time - keyframe.time >= first.time - time {
                first = keyframe;
            }

            if keyframe.time > time &&
               (keyframe.time - time < second.time - time || second.time < time) {
                second = keyframe;
            }
        }

        let keyframe_length = second.time - first.time;
        let alpha = (time - first.time) / keyframe_length;

        (first.clone(), second.clone(), alpha)
    }

    fn lerp_camera(camera: &Camera, time: f64) -> Camera {
        let keyframes = match camera.keyframes.clone() {
            Some(k) => k,
            None => fail!("Cannot lerp a camera with no keyframes!")
        };

        let (first, second, alpha) = Animator::get_neighbour_keyframes(keyframes, time);

        let lerped_position = Vec3::lerp(&first.position, &second.position, alpha);
        let lerped_look_at  = Vec3::lerp(&first.look_at, &second.look_at, alpha);
        let lerped_up       = Vec3::lerp(&first.up, &second.up, alpha);

        let mut lerped_camera = Camera::new(
            lerped_position,
            lerped_look_at,
            lerped_up,
            camera.fov_deg,
            camera.image_width,
            camera.image_height,
        );

        lerped_camera.keyframes = camera.keyframes.clone();
        lerped_camera
    }
}
<commit_msg>Optimise animator to write out frames as a separate task.<commit_after>use raytracer::animator::CameraKeyframe;
use raytracer::Renderer;
use scene::{Camera, Scene};
use std::sync::Arc;
use vec3::Vec3;

pub struct Animator {
    pub fps: f64,
    pub length: f64, // rounded down to nearest frame
    pub renderer: Renderer
}

// TODO: Non-linear interpolation
// TODO: Improve keyframes (sort/order them as we don't need dynamic keyframe insertion)
impl Animator {
    // TODO: make this a Surface iterator so both single frame and animation
    // process flows are similar
    pub fn animate(&self, camera: Camera, shared_scene: Arc<Scene>, filename: &str) {
        let start_time = ::time::get_time();
        let total_frames = (self.fps * self.length).floor() as uint;

        let (tx, rx) = sync_channel(1);
        tx.send(1i);

        for frame_number in range(0, total_frames) {
            let time = frame_number as f64 / self.fps;
            let lerped_camera = Animator::lerp_camera(&camera, time);
            let frame_data = self.renderer.render(lerped_camera, shared_scene.clone());

            let shared_name = format!("{}{:06u}.ppm", filename, frame_number);
            let child_tx = tx.clone();

            rx.recv();
            spawn(proc() {
                ::util::export::to_ppm(frame_data, shared_name.as_slice());
                child_tx.send(2i);
            });

            ::util::print_progress("*** Frame", start_time, frame_number + 1 as uint, total_frames);
            println!("");
        }

        rx.recv();
    }

    fn get_neighbour_keyframes(keyframes: Vec<CameraKeyframe>, time: f64)
                               -> (CameraKeyframe, CameraKeyframe, f64) {

        if keyframes.len() <= 1 {
            fail!("Not enough keyframes to interpolate: got: {} expected: >= 2", keyframes.len());
        }

        // Get the two keyframes inbetween current time
        let mut first = &keyframes[0];
        let mut second = &keyframes[1];

        for keyframe in keyframes.iter() {
            if keyframe.time <= time && time - keyframe.time >= first.time - time {
                first = keyframe;
            }

            if keyframe.time > time &&
               (keyframe.time - time < second.time - time || second.time < time) {
                second = keyframe;
            }
        }

        let keyframe_length = second.time - first.time;
        let alpha = (time - first.time) / keyframe_length;

        (first.clone(), second.clone(), alpha)
    }

    fn lerp_camera(camera: &Camera, time: f64) -> Camera {
        let keyframes = match camera.keyframes.clone() {
            Some(k) => k,
            None => fail!("Cannot lerp a camera with no keyframes!")
        };

        let (first, second, alpha) = Animator::get_neighbour_keyframes(keyframes, time);

        let lerped_position = Vec3::lerp(&first.position, &second.position, alpha);
        let lerped_look_at  = Vec3::lerp(&first.look_at, &second.look_at, alpha);
        let lerped_up       = Vec3::lerp(&first.up, &second.up, alpha);

        let mut lerped_camera = Camera::new(
            lerped_position,
            lerped_look_at,
            lerped_up,
            camera.fov_deg,
            camera.image_width,
            camera.image_height,
        );

        lerped_camera.keyframes = camera.keyframes.clone();
        lerped_camera
    }
}
<|endoftext|>