<commit_before>use std;
#[cfg(feature = "legacy-runtime")]
use std::any::Any;
use std::boxed::Box;
use std::cell::Cell;
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::panic::UnwindSafe;
#[cfg(feature = "legacy-runtime")]
use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
use neon_runtime;
use neon_runtime::raw;
use neon_runtime::scope::Root;
#[cfg(feature = "legacy-runtime")]
use neon_runtime::try_catch::TryCatchControl;
use types::{JsObject, JsValue};
#[cfg(feature = "napi-runtime")]
use types::JsUndefined;
use handle::Handle;
#[cfg(feature = "legacy-runtime")]
use handle::Managed;
use object::class::ClassMap;
use result::{JsResult, NeonResult};
use super::ModuleContext;

#[cfg(feature = "legacy-runtime")]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Env(raw::Isolate);

#[cfg(feature = "napi-runtime")]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Env(raw::Env);

extern "C" fn drop_class_map(map: Box<ClassMap>) {
    std::mem::drop(map);
}

impl Env {
    #[cfg(feature = "legacy-runtime")]
    pub(crate) fn to_raw(self) -> raw::Isolate {
        let Self(ptr) = self;
        ptr
    }

    #[cfg(feature = "napi-runtime")]
    pub(crate) fn to_raw(self) -> raw::Env {
        let Self(ptr) = self;
        ptr
    }

    pub(crate) fn class_map(&mut self) -> &mut ClassMap {
        let mut ptr: *mut c_void = unsafe { neon_runtime::class::get_class_map(self.to_raw()) };
        if ptr.is_null() {
            let b: Box<ClassMap> = Box::new(ClassMap::new());
            let raw = Box::into_raw(b);
            ptr = unsafe { std::mem::transmute(raw) };
            let free_map: *mut c_void = unsafe { std::mem::transmute(drop_class_map as usize) };
            unsafe {
                neon_runtime::class::set_class_map(self.to_raw(), ptr, free_map);
            }
        }
        unsafe { std::mem::transmute(ptr) }
    }

    #[cfg(feature = "napi-runtime")]
    pub(crate) fn current() -> Env {
        panic!("Context::current() will not implemented with n-api")
    }

    #[cfg(feature = "legacy-runtime")]
    pub(crate) fn current() -> Env {
        unsafe {
            std::mem::transmute(neon_runtime::call::current_isolate())
        }
    }
}

pub struct ScopeMetadata {
    env: Env,
    active: Cell<bool>
}

pub struct Scope<'a, R: Root + 'static> {
    pub metadata: ScopeMetadata,
    pub handle_scope: &'a mut R
}

impl<'a, R: Root + 'static> Scope<'a, R> {
    pub fn with<T, F: for<'b> FnOnce(Scope<'b, R>) -> T>(env: Env, f: F) -> T {
        let mut handle_scope: R = unsafe { R::allocate() };
        unsafe {
            handle_scope.enter(env.to_raw());
        }
        let result = {
            let scope = Scope {
                metadata: ScopeMetadata {
                    env,
                    active: Cell::new(true)
                },
                handle_scope: &mut handle_scope
            };
            f(scope)
        };
        unsafe {
            handle_scope.exit(env.to_raw());
        }
        result
    }
}

pub trait ContextInternal<'a>: Sized {
    fn scope_metadata(&self) -> &ScopeMetadata;

    fn env(&self) -> Env {
        self.scope_metadata().env
    }

    fn is_active(&self) -> bool {
        self.scope_metadata().active.get()
    }

    fn check_active(&self) {
        if !self.is_active() {
            panic!("execution context is inactive");
        }
    }

    fn activate(&self) { self.scope_metadata().active.set(true); }
    fn deactivate(&self) { self.scope_metadata().active.set(false); }

    #[cfg(feature = "legacy-runtime")]
    fn try_catch_internal<'b: 'a, F>(&mut self, f: F) -> Result<Handle<'a, JsValue>, Handle<'a, JsValue>>
        where F: UnwindSafe + FnOnce(&mut Self) -> JsResult<'b, JsValue>
    {
        // A closure does not have a guaranteed layout, so we need to box it in order to pass
        // a pointer to it across the boundary into C++.
        let rust_thunk = Box::into_raw(Box::new(f));

        let mut local: MaybeUninit<raw::Local> = MaybeUninit::zeroed();
        let mut unwind_value: MaybeUninit<*mut c_void> = MaybeUninit::zeroed();

        let ctrl = unsafe {
            neon_runtime::try_catch::with(try_catch_glue::<Self, F>,
                                          rust_thunk as *mut c_void,
                                          (self as *mut Self) as *mut c_void,
                                          local.as_mut_ptr(),
                                          unwind_value.as_mut_ptr())
        };

        match ctrl {
            TryCatchControl::Panicked => {
                let unwind_value: Box<dyn Any + Send> = *unsafe {
                    Box::from_raw(unwind_value.assume_init() as *mut Box<dyn Any + Send>)
                };
                resume_unwind(unwind_value);
            }
            TryCatchControl::Returned => {
                let local = unsafe { local.assume_init() };
                Ok(JsValue::new_internal(local))
            }
            TryCatchControl::Threw => {
                let local = unsafe { local.assume_init() };
                Err(JsValue::new_internal(local))
            }
            TryCatchControl::UnexpectedErr => {
                panic!("try_catch: unexpected Err(Throw) when VM is not in a throwing state");
            }
        }
    }

    #[cfg(feature = "napi-runtime")]
    fn try_catch_internal<'b: 'a, F>(&mut self, f: F) -> Result<Handle<'a, JsValue>, Handle<'a, JsValue>>
        where F: UnwindSafe + FnOnce(&mut Self) -> JsResult<'b, JsValue>
    {
        let result = f(self);
        let mut local: MaybeUninit<raw::Local> = MaybeUninit::zeroed();
        unsafe {
            if neon_runtime::error::catch_error(self.env().to_raw(), local.as_mut_ptr()) {
                Err(JsValue::new_internal(local.assume_init()))
            } else if let Ok(result) = result {
                Ok(result)
            } else {
                panic!("try_catch: unexpected Err(Throw) when VM is not in a throwing state");
            }
        }
    }
}

#[cfg(feature = "legacy-runtime")]
extern "C" fn try_catch_glue<'a, 'b: 'a, C, F>(rust_thunk: *mut c_void,
                                               cx: *mut c_void,
                                               returned: *mut raw::Local,
                                               unwind_value: *mut *mut c_void) -> TryCatchControl
    where C: ContextInternal<'a>,
          F: UnwindSafe + FnOnce(&mut C) -> JsResult<'b, JsValue>
{
    let f: F = *unsafe { Box::from_raw(rust_thunk as *mut F) };
    let cx: &mut C = unsafe { std::mem::transmute(cx) };

    // The mutable reference to the context is a fiction of the Neon library,
    // since it doesn't actually contain any data in the Rust memory space,
    // just a link to the JS VM. So we don't need to do any kind of poisoning
    // of the context when a panic occurs. So we suppress the Rust compiler
    // errors from using the mutable reference across an unwind boundary.
    match catch_unwind(AssertUnwindSafe(|| f(cx))) {
        // No Rust panic, no JS exception.
        Ok(Ok(result)) => unsafe {
            *returned = result.to_raw();
            TryCatchControl::Returned
        }
        // No Rust panic, caught a JS exception.
        Ok(Err(_)) => {
            TryCatchControl::Threw
        }
        // Rust panicked.
        Err(err) => unsafe {
            // A panic value has an undefined layout, so wrap it in an extra box.
            let boxed = Box::new(err);
            *unwind_value = Box::into_raw(boxed) as *mut c_void;
            TryCatchControl::Panicked
        }
    }
}

#[cfg(feature = "legacy-runtime")]
pub fn initialize_module(exports: Handle<JsObject>, init: fn(ModuleContext) -> NeonResult<()>) {
    let env = Env::current();

    ModuleContext::with(env, exports, |cx| {
        let _ = init(cx);
    });
}

#[cfg(feature = "napi-runtime")]
pub fn initialize_module(env: raw::Env, exports: Handle<JsObject>, init: fn(ModuleContext) -> NeonResult<()>) {
    ModuleContext::with(Env(env), exports, |cx| {
        let _ = init(cx);
    });
}
<commit_msg>Remove unnecessary `UnwindSafe` restriction for N-API backend.<commit_after>use std;
#[cfg(feature = "legacy-runtime")]
use std::any::Any;
use std::boxed::Box;
use std::cell::Cell;
use std::mem::MaybeUninit;
use std::os::raw::c_void;
#[cfg(feature = "legacy-runtime")]
use std::panic::{AssertUnwindSafe, UnwindSafe, catch_unwind, resume_unwind};
use neon_runtime;
use neon_runtime::raw;
use neon_runtime::scope::Root;
#[cfg(feature = "legacy-runtime")]
use neon_runtime::try_catch::TryCatchControl;
use types::{JsObject, JsValue};
#[cfg(feature = "napi-runtime")]
use types::JsUndefined;
use handle::Handle;
#[cfg(feature = "legacy-runtime")]
use handle::Managed;
use object::class::ClassMap;
use result::{JsResult, NeonResult};
use super::ModuleContext;

#[cfg(feature = "legacy-runtime")]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Env(raw::Isolate);

#[cfg(feature = "napi-runtime")]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Env(raw::Env);

extern "C" fn drop_class_map(map: Box<ClassMap>) {
    std::mem::drop(map);
}

impl Env {
    #[cfg(feature = "legacy-runtime")]
    pub(crate) fn to_raw(self) -> raw::Isolate {
        let Self(ptr) = self;
        ptr
    }

    #[cfg(feature = "napi-runtime")]
    pub(crate) fn to_raw(self) -> raw::Env {
        let Self(ptr) = self;
        ptr
    }

    pub(crate) fn class_map(&mut self) -> &mut ClassMap {
        let mut ptr: *mut c_void = unsafe { neon_runtime::class::get_class_map(self.to_raw()) };
        if ptr.is_null() {
            let b: Box<ClassMap> = Box::new(ClassMap::new());
            let raw = Box::into_raw(b);
            ptr = unsafe { std::mem::transmute(raw) };
            let free_map: *mut c_void = unsafe { std::mem::transmute(drop_class_map as usize) };
            unsafe {
                neon_runtime::class::set_class_map(self.to_raw(), ptr, free_map);
            }
        }
        unsafe { std::mem::transmute(ptr) }
    }

    #[cfg(feature = "napi-runtime")]
    pub(crate) fn current() -> Env {
        panic!("Context::current() will not implemented with n-api")
    }

    #[cfg(feature = "legacy-runtime")]
    pub(crate) fn current() -> Env {
        unsafe {
            std::mem::transmute(neon_runtime::call::current_isolate())
        }
    }
}

pub struct ScopeMetadata {
    env: Env,
    active: Cell<bool>
}

pub struct Scope<'a, R: Root + 'static> {
    pub metadata: ScopeMetadata,
    pub handle_scope: &'a mut R
}

impl<'a, R: Root + 'static> Scope<'a, R> {
    pub fn with<T, F: for<'b> FnOnce(Scope<'b, R>) -> T>(env: Env, f: F) -> T {
        let mut handle_scope: R = unsafe { R::allocate() };
        unsafe {
            handle_scope.enter(env.to_raw());
        }
        let result = {
            let scope = Scope {
                metadata: ScopeMetadata {
                    env,
                    active: Cell::new(true)
                },
                handle_scope: &mut handle_scope
            };
            f(scope)
        };
        unsafe {
            handle_scope.exit(env.to_raw());
        }
        result
    }
}

pub trait ContextInternal<'a>: Sized {
    fn scope_metadata(&self) -> &ScopeMetadata;

    fn env(&self) -> Env {
        self.scope_metadata().env
    }

    fn is_active(&self) -> bool {
        self.scope_metadata().active.get()
    }

    fn check_active(&self) {
        if !self.is_active() {
            panic!("execution context is inactive");
        }
    }

    fn activate(&self) { self.scope_metadata().active.set(true); }
    fn deactivate(&self) { self.scope_metadata().active.set(false); }

    #[cfg(feature = "legacy-runtime")]
    fn try_catch_internal<'b: 'a, F>(&mut self, f: F) -> Result<Handle<'a, JsValue>, Handle<'a, JsValue>>
        where F: UnwindSafe + FnOnce(&mut Self) -> JsResult<'b, JsValue>
    {
        // A closure does not have a guaranteed layout, so we need to box it in order to pass
        // a pointer to it across the boundary into C++.
        let rust_thunk = Box::into_raw(Box::new(f));

        let mut local: MaybeUninit<raw::Local> = MaybeUninit::zeroed();
        let mut unwind_value: MaybeUninit<*mut c_void> = MaybeUninit::zeroed();

        let ctrl = unsafe {
            neon_runtime::try_catch::with(try_catch_glue::<Self, F>,
                                          rust_thunk as *mut c_void,
                                          (self as *mut Self) as *mut c_void,
                                          local.as_mut_ptr(),
                                          unwind_value.as_mut_ptr())
        };

        match ctrl {
            TryCatchControl::Panicked => {
                let unwind_value: Box<dyn Any + Send> = *unsafe {
                    Box::from_raw(unwind_value.assume_init() as *mut Box<dyn Any + Send>)
                };
                resume_unwind(unwind_value);
            }
            TryCatchControl::Returned => {
                let local = unsafe { local.assume_init() };
                Ok(JsValue::new_internal(local))
            }
            TryCatchControl::Threw => {
                let local = unsafe { local.assume_init() };
                Err(JsValue::new_internal(local))
            }
            TryCatchControl::UnexpectedErr => {
                panic!("try_catch: unexpected Err(Throw) when VM is not in a throwing state");
            }
        }
    }

    #[cfg(feature = "napi-runtime")]
    fn try_catch_internal<'b: 'a, F>(&mut self, f: F) -> Result<Handle<'a, JsValue>, Handle<'a, JsValue>>
        where F: FnOnce(&mut Self) -> JsResult<'b, JsValue>
    {
        let result = f(self);
        let mut local: MaybeUninit<raw::Local> = MaybeUninit::zeroed();
        unsafe {
            if neon_runtime::error::catch_error(self.env().to_raw(), local.as_mut_ptr()) {
                Err(JsValue::new_internal(local.assume_init()))
            } else if let Ok(result) = result {
                Ok(result)
            } else {
                panic!("try_catch: unexpected Err(Throw) when VM is not in a throwing state");
            }
        }
    }
}

#[cfg(feature = "legacy-runtime")]
extern "C" fn try_catch_glue<'a, 'b: 'a, C, F>(rust_thunk: *mut c_void,
                                               cx: *mut c_void,
                                               returned: *mut raw::Local,
                                               unwind_value: *mut *mut c_void) -> TryCatchControl
    where C: ContextInternal<'a>,
          F: UnwindSafe + FnOnce(&mut C) -> JsResult<'b, JsValue>
{
    let f: F = *unsafe { Box::from_raw(rust_thunk as *mut F) };
    let cx: &mut C = unsafe { std::mem::transmute(cx) };

    // The mutable reference to the context is a fiction of the Neon library,
    // since it doesn't actually contain any data in the Rust memory space,
    // just a link to the JS VM. So we don't need to do any kind of poisoning
    // of the context when a panic occurs. So we suppress the Rust compiler
    // errors from using the mutable reference across an unwind boundary.
    match catch_unwind(AssertUnwindSafe(|| f(cx))) {
        // No Rust panic, no JS exception.
        Ok(Ok(result)) => unsafe {
            *returned = result.to_raw();
            TryCatchControl::Returned
        }
        // No Rust panic, caught a JS exception.
        Ok(Err(_)) => {
            TryCatchControl::Threw
        }
        // Rust panicked.
        Err(err) => unsafe {
            // A panic value has an undefined layout, so wrap it in an extra box.
            let boxed = Box::new(err);
            *unwind_value = Box::into_raw(boxed) as *mut c_void;
            TryCatchControl::Panicked
        }
    }
}

#[cfg(feature = "legacy-runtime")]
pub fn initialize_module(exports: Handle<JsObject>, init: fn(ModuleContext) -> NeonResult<()>) {
    let env = Env::current();

    ModuleContext::with(env, exports, |cx| {
        let _ = init(cx);
    });
}

#[cfg(feature = "napi-runtime")]
pub fn initialize_module(env: raw::Env, exports: Handle<JsObject>, init: fn(ModuleContext) -> NeonResult<()>) {
    ModuleContext::with(Env(env), exports, |cx| {
        let _ = init(cx);
    });
}
<|endoftext|>


<commit_before>extern crate websocket;
extern crate futures;
extern crate futures_cpupool;
extern crate tokio_core;
extern crate serde;
extern crate serde_json;
extern crate dotenv;
extern crate byteorder;

use self::dotenv::dotenv;

pub use self::websocket::message::OwnedMessage;
use self::websocket::server::{WsServer, InvalidConnection};
use self::websocket::async::{Server, MessageCodec};
use self::websocket::client::async::Framed;
use self::websocket::server::NoTlsAcceptor;

use self::tokio_core::reactor::{Handle, Remote, Core, Interval};
use self::tokio_core::net::{TcpStream, TcpListener};

use self::futures::{Future, Sink, Stream};
use self::futures::sync::mpsc;
use self::futures_cpupool::CpuPool;

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

use std::sync::{RwLock, Arc};
use std::fmt::Debug;
use std::ops::Deref;
use std::collections::HashMap;
use std::io::Cursor;
use std::time::Duration;

use model::client::ClientState;
use util::{timestamp, read_env_var};

pub type Id = u32;

type SinkContent = Framed<TcpStream, MessageCodec<OwnedMessage>>;
type SplitSink = futures::stream::SplitSink<SinkContent>;

#[derive(Clone)]
pub struct ConnectionData {
    pub ping: u64,
    pub send_channel: mpsc::UnboundedSender<OwnedMessage>,
}
type SyncConnections = HashMap<Id, RwLock<ConnectionData>>;
type Connections = Arc<RwLock<SyncConnections>>;

#[derive(Clone)]
pub struct Engine {
    pub remote: Remote,
    pub connections: Connections,
}

#[derive(Debug)]
pub struct Msg {
    pub id: Id,
    pub content: OwnedMessage,
}

pub trait EventHandler {
    fn new(engine: Engine) -> Self;
    fn main_loop(&self);
    fn message(&self, msg: &Msg);
    fn connect(&self, id: Id) -> bool;
    fn disconnect(&self, id: Id);
}

pub fn execute<T>()
where
    T: EventHandler + Send + Sync + 'static,
{
    dotenv().ok();

    let mut core = Core::new().expect("Failed to create Tokio event loop");
    let handle = core.handle();
    let remote = core.remote();

    let server = build_server(&handle);
    let pool = CpuPool::new(4);
    let connections = Arc::new(RwLock::new(HashMap::new()));
    let (receive_channel_out, receive_channel_in) = mpsc::unbounded();
    let (send_channel_out, send_channel_in) = mpsc::unbounded();

    let ping_timestamps = Arc::new(RwLock::new(HashMap::new()));
    let id_gen = Arc::new(RwLock::new(IdGen::new()));

    let engine = Engine {
        connections: connections.clone(),
        remote: remote.clone(),
    };
    let event_handler = T::new(engine);
    let event_handler = Arc::new(event_handler);

    let id_gen_inner = id_gen.clone();
    let connections_inner = connections.clone();
    let event_handler_inner = event_handler.clone();
    let handle_inner = handle.clone();
    // Handle new connection
    let connection_handler = server
        .incoming()
        .map(|(upgrade, addr)| Some((upgrade, addr)))
        .or_else(|InvalidConnection { error, .. }| {
            println!("Connection dropped: {}", error);
            Ok(None)
        })
        .for_each(move |conn| {
            if conn.is_none() {
                return Ok(());
            }
            let (upgrade, addr) = conn.unwrap();
            let event_handler = event_handler_inner.clone();
            let connections_inner = connections_inner.clone();
            let receive_channel = receive_channel_out.clone();
            let send_channel = send_channel_out.clone();
            let id_gen = id_gen_inner.clone();
            let f = upgrade.accept().and_then(move |(framed, _)| {
                let id = id_gen.write().unwrap().next().expect(
                    "maximum amount of ids reached",
                );
                println!("Got a connection from: {}, assigned id {}", addr, id);
                if !event_handler.connect(id) {
                    return Ok(());
                }
                let (sink, stream) = framed.split();
                let (conn_out, conn_in) = mpsc::unbounded();
                send_channel.send((id, conn_in, sink)).wait().unwrap();
                receive_channel.send((id, stream)).wait().unwrap();
                let data = ConnectionData {
                    ping: 0,
                    send_channel: conn_out,
                };
                connections_inner.write().unwrap().insert(
                    id,
                    RwLock::new(data),
                );
                Ok(())
            });
            spawn_future(f, "Handle new connection", &handle_inner);
            Ok(())
        })
        .map_err(|e: ()| e);

    // Handle receiving messages from a client
    let connections_inner = connections.clone();
    let event_handler_inner = event_handler.clone();
    let remote_inner = remote.clone();
    let ping_timestamps_inner = ping_timestamps.clone();
    let receive_handler = pool.spawn_fn(|| {
        receive_channel_in.for_each(move |(id, stream)| {
            let connections = connections_inner.clone();
            let event_handler = event_handler_inner.clone();
            let ping_timestamps = ping_timestamps_inner.clone();
            remote_inner.spawn(move |_| {
                stream
                    .for_each(move |msg| {
                        if let OwnedMessage::Close(_) = msg {
                            kick(&connections, id, &*event_handler);
                        } else {
                            let message = Msg {
                                id: id,
                                content: msg
                            };
                            event_handler.message(&message);
                        }
                        Ok(())
                    })
                    .map_err(|e| println!("Error while receiving messages: {}", e))
            });
            Ok(())
        })
    }).map_err(|e| println!("Error while receiving messages: {:?}", e));

    // Handle sending messages to a client
    let event_handler_inner = event_handler.clone();
    let connections_inner = connections.clone();
    let send_handler = pool.spawn_fn(move || {
        let remote = remote.clone();
        let connections = connections_inner.clone();
        let event_handler = event_handler_inner.clone();
        send_channel_in.for_each(
            move |(id, conn_in, sink): (Id,
                                        mpsc::UnboundedReceiver<OwnedMessage>,
                                        SplitSink)| {
                let sink = Arc::new(RwLock::new(sink));
                let connections = connections.clone();
                let event_handler = event_handler.clone();
                remote.spawn(move |_| {
                    conn_in.for_each(move |msg: OwnedMessage| {
                        let mut sink = sink.write().unwrap();
                        let ok_send = sink.start_send(msg).is_ok();
                        let ok_poll = sink.poll_complete().is_ok();
                        if !ok_send || !ok_poll {
                            println!("Failed to send, kicking client {}", id);
                            kick(&connections, id, &*event_handler);
                        }
                        Ok(())
                    })
                });
                Ok(())
            },
        )
    }).map_err(|e| println!("Error while sending messages: {:?}", e));

    // Run maIn loop
    let main_fn = pool.spawn_fn(move || {
        event_handler.main_loop();
        Ok::<(), ()>(())
    }).map_err(|e| println!("Error in main callback function: {:?}", e));

    let handlers = main_fn.select2(connection_handler.select2(
        receive_handler.select(send_handler),
    ));

    Interval::new(Duration::from_millis(500), &handle)
    .and_then(move |interval| {
        let ping_timestamps = ping_timestamps.clone();
        let id_gen = id_gen.clone();
        let connections = connections.clone();
        let f = interval.for_each(move |_| {
            let connections = connections.read().unwrap();
            for (_, connection) in connections.iter() {
                let connection = connection.write().unwrap();
                let mut ping_timestamps = ping_timestamps.write().unwrap();
                let signature = id_gen.write().unwrap().next().expect("Reached id generation limit");
                ping_timestamps.insert(signature, timestamp());
            }
            Ok(())
        });
        spawn_future(f, "Ping interval", &handle);
        Ok(())
    }).map_err(|e| println!("Error in setting pinger interval up: {:?}", e));

    core.run(handlers)
        .map_err(|_| println!("Unspecified error while running core loop"))
        .unwrap();
}

pub fn spawn_future<F, I, E>(f: F, desc: &'static str, handle: &Handle)
where
    F: Future<Item = I, Error = E> + 'static,
    E: Debug,
{
    handle.spawn(
        f.map_err(move |e| println!("Error in {}: '{:?}'", desc, e))
            .map(move |_| ()),
    );
}

fn build_server(handle: &Handle) -> WsServer<NoTlsAcceptor, TcpListener> {
    let address = format!("localhost:{}", read_env_var("CORE_PORT"));
    Server::bind(address, handle).expect("Failed to create server")
}

fn handle_pong_signature(
    id: Id,
    signature: &Vec<u8>,
    connections: Connections,
    ping_timestamps: Arc<RwLock<HashMap<Id, u64>>>,
) {
    let connections = connections.read().unwrap();
    let mut ping_timestamps = ping_timestamps.write().unwrap();
    let mut signature = Cursor::new(&signature);
    if let Ok(signature) = signature.read_u32::<BigEndian>() {
        if let Some(ping_timestamp) = ping_timestamps.remove(&signature) {
            let ping = timestamp() - ping_timestamp;
            connections
                .get(&id)
                .expect("Tried to access invalid id")
                .write()
                .unwrap()
                .ping = ping;
        }
    }
}

fn kick<T>(connections: &Connections, id: Id, event_handler: &T)
where
    T: EventHandler,
{
    connections
        .write()
        .unwrap()
        .remove(&id)
        .and_then(|_| Some(println!("Client with id {} disconnected", id)))
        .expect("Tried to remove id that was not in list");
    event_handler.disconnect(id);
}

struct IdGen {
    count: Id,
}
impl IdGen {
    fn new() -> Self {
        IdGen { count: 0 }
    }
}
impl Iterator for IdGen {
    type Item = Id;

    fn next(&mut self) -> Option<Id> {
        if self.count != Id::max_value() {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

#[test]
fn id_gen_unique() {
    let mut id_gen = IdGen::new();
    let mut ids = Vec::new();
    let ids_count = 1000;
    for _ in 0..ids_count {
        ids.push(id_gen.next());
    }
    ids.sort();
    ids.dedup_by_key(|id| *id);
    assert_eq!(ids_count, ids.len());
}
<commit_msg>Core: Run rustfmt<commit_after>extern crate websocket;
extern crate futures;
extern crate futures_cpupool;
extern crate tokio_core;
extern crate serde;
extern crate serde_json;
extern crate dotenv;
extern crate byteorder;

use self::dotenv::dotenv;

pub use self::websocket::message::OwnedMessage;
use self::websocket::server::{WsServer, InvalidConnection};
use self::websocket::async::{Server, MessageCodec};
use self::websocket::client::async::Framed;
use self::websocket::server::NoTlsAcceptor;

use self::tokio_core::reactor::{Handle, Remote, Core, Interval};
use self::tokio_core::net::{TcpStream, TcpListener};

use self::futures::{Future, Sink, Stream};
use self::futures::sync::mpsc;
use self::futures_cpupool::CpuPool;

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

use std::sync::{RwLock, Arc};
use std::fmt::Debug;
use std::ops::Deref;
use std::collections::HashMap;
use std::io::Cursor;
use std::time::Duration;

use model::client::ClientState;
use util::{timestamp, read_env_var};

pub type Id = u32;

type SinkContent = Framed<TcpStream, MessageCodec<OwnedMessage>>;
type SplitSink = futures::stream::SplitSink<SinkContent>;

#[derive(Clone)]
pub struct ConnectionData {
    pub ping: u64,
    pub send_channel: mpsc::UnboundedSender<OwnedMessage>,
}
type SyncConnections = HashMap<Id, RwLock<ConnectionData>>;
type Connections = Arc<RwLock<SyncConnections>>;

#[derive(Clone)]
pub struct Engine {
    pub remote: Remote,
    pub connections: Connections,
}

#[derive(Debug)]
pub struct Msg {
    pub id: Id,
    pub content: OwnedMessage,
}

pub trait EventHandler {
    fn new(engine: Engine) -> Self;
    fn main_loop(&self);
    fn message(&self, msg: &Msg);
    fn connect(&self, id: Id) -> bool;
    fn disconnect(&self, id: Id);
}

pub fn execute<T>()
where
    T: EventHandler + Send + Sync + 'static,
{
    dotenv().ok();

    let mut core = Core::new().expect("Failed to create Tokio event loop");
    let handle = core.handle();
    let remote = core.remote();

    let server = build_server(&handle);
    let pool = CpuPool::new(4);
    let connections = Arc::new(RwLock::new(HashMap::new()));
    let (receive_channel_out, receive_channel_in) = mpsc::unbounded();
    let (send_channel_out, send_channel_in) = mpsc::unbounded();

    let ping_timestamps = Arc::new(RwLock::new(HashMap::new()));
    let id_gen = Arc::new(RwLock::new(IdGen::new()));

    let engine = Engine {
        connections: connections.clone(),
        remote: remote.clone(),
    };
    let event_handler = T::new(engine);
    let event_handler = Arc::new(event_handler);

    let id_gen_inner = id_gen.clone();
    let connections_inner = connections.clone();
    let event_handler_inner = event_handler.clone();
    let handle_inner = handle.clone();
    // Handle new connection
    let connection_handler = server
        .incoming()
        .map(|(upgrade, addr)| Some((upgrade, addr)))
        .or_else(|InvalidConnection { error, .. }| {
            println!("Connection dropped: {}", error);
            Ok(None)
        })
        .for_each(move |conn| {
            if conn.is_none() {
                return Ok(());
            }
            let (upgrade, addr) = conn.unwrap();
            let event_handler = event_handler_inner.clone();
            let connections_inner = connections_inner.clone();
            let receive_channel = receive_channel_out.clone();
            let send_channel = send_channel_out.clone();
            let id_gen = id_gen_inner.clone();
            let f = upgrade.accept().and_then(move |(framed, _)| {
                let id = id_gen.write().unwrap().next().expect(
                    "maximum amount of ids reached",
                );
                println!("Got a connection from: {}, assigned id {}", addr, id);
                if !event_handler.connect(id) {
                    return Ok(());
                }
                let (sink, stream) = framed.split();
                let (conn_out, conn_in) = mpsc::unbounded();
                send_channel.send((id, conn_in, sink)).wait().unwrap();
                receive_channel.send((id, stream)).wait().unwrap();
                let data = ConnectionData {
                    ping: 0,
                    send_channel: conn_out,
                };
                connections_inner.write().unwrap().insert(
                    id,
                    RwLock::new(data),
                );
                Ok(())
            });
            spawn_future(f, "Handle new connection", &handle_inner);
            Ok(())
        })
        .map_err(|e: ()| e);

    // Handle receiving messages from a client
    let connections_inner = connections.clone();
    let event_handler_inner = event_handler.clone();
    let remote_inner = remote.clone();
    let ping_timestamps_inner = ping_timestamps.clone();
    let receive_handler = pool.spawn_fn(|| {
        receive_channel_in.for_each(move |(id, stream)| {
            let connections = connections_inner.clone();
            let event_handler = event_handler_inner.clone();
            let ping_timestamps = ping_timestamps_inner.clone();
            remote_inner.spawn(move |_| {
                stream
                    .for_each(move |msg| {
                        if let OwnedMessage::Close(_) = msg {
                            kick(&connections, id, &*event_handler);
                        } else {
                            let message = Msg {
                                id: id,
                                content: msg,
                            };
                            event_handler.message(&message);
                        }
                        Ok(())
                    })
                    .map_err(|e| println!("Error while receiving messages: {}", e))
            });
            Ok(())
        })
    }).map_err(|e| println!("Error while receiving messages: {:?}", e));

    // Handle sending messages to a client
    let event_handler_inner = event_handler.clone();
    let connections_inner = connections.clone();
    let send_handler = pool.spawn_fn(move || {
        let remote = remote.clone();
        let connections = connections_inner.clone();
        let event_handler = event_handler_inner.clone();
        send_channel_in.for_each(
            move |(id, conn_in, sink): (Id,
                                        mpsc::UnboundedReceiver<OwnedMessage>,
                                        SplitSink)| {
                let sink = Arc::new(RwLock::new(sink));
                let connections = connections.clone();
                let event_handler = event_handler.clone();
                remote.spawn(move |_| {
                    conn_in.for_each(move |msg: OwnedMessage| {
                        let mut sink = sink.write().unwrap();
                        let ok_send = sink.start_send(msg).is_ok();
                        let ok_poll = sink.poll_complete().is_ok();
                        if !ok_send || !ok_poll {
                            println!("Failed to send, kicking client {}", id);
                            kick(&connections, id, &*event_handler);
                        }
                        Ok(())
                    })
                });
                Ok(())
            },
        )
    }).map_err(|e| println!("Error while sending messages: {:?}", e));

    // Run maIn loop
    let main_fn = pool.spawn_fn(move || {
        event_handler.main_loop();
        Ok::<(), ()>(())
    }).map_err(|e| println!("Error in main callback function: {:?}", e));

    let handlers = main_fn.select2(connection_handler.select2(
        receive_handler.select(send_handler),
    ));

    Interval::new(Duration::from_millis(500), &handle)
        .and_then(move |interval| {
            let ping_timestamps = ping_timestamps.clone();
            let id_gen = id_gen.clone();
            let connections = connections.clone();
            let f = interval.for_each(move |_| {
                let connections = connections.read().unwrap();
                for (_, connection) in connections.iter() {
                    let connection = connection.write().unwrap();
                    let mut ping_timestamps = ping_timestamps.write().unwrap();
                    let signature = id_gen.write().unwrap().next().expect(
                        "Reached id generation limit",
                    );
                    ping_timestamps.insert(signature, timestamp());
                }
                Ok(())
            });
            spawn_future(f, "Ping interval", &handle);
            Ok(())
        })
        .map_err(|e| println!("Error in setting pinger interval up: {:?}", e));

    core.run(handlers)
        .map_err(|_| println!("Unspecified error while running core loop"))
        .unwrap();
}

pub fn spawn_future<F, I, E>(f: F, desc: &'static str, handle: &Handle)
where
    F: Future<Item = I, Error = E> + 'static,
    E: Debug,
{
    handle.spawn(
        f.map_err(move |e| println!("Error in {}: '{:?}'", desc, e))
            .map(move |_| ()),
    );
}

fn build_server(handle: &Handle) -> WsServer<NoTlsAcceptor, TcpListener> {
    let address = format!("localhost:{}", read_env_var("CORE_PORT"));
    Server::bind(address, handle).expect("Failed to create server")
}

fn handle_pong_signature(
    id: Id,
    signature: &Vec<u8>,
    connections: Connections,
    ping_timestamps: Arc<RwLock<HashMap<Id, u64>>>,
) {
    let connections = connections.read().unwrap();
    let mut ping_timestamps = ping_timestamps.write().unwrap();
    let mut signature = Cursor::new(&signature);
    if let Ok(signature) = signature.read_u32::<BigEndian>() {
        if let Some(ping_timestamp) = ping_timestamps.remove(&signature) {
            let ping = timestamp() - ping_timestamp;
            connections
                .get(&id)
                .expect("Tried to access invalid id")
                .write()
                .unwrap()
                .ping = ping;
        }
    }
}

fn kick<T>(connections: &Connections, id: Id, event_handler: &T)
where
    T: EventHandler,
{
    connections
        .write()
        .unwrap()
        .remove(&id)
        .and_then(|_| Some(println!("Client with id {} disconnected", id)))
        .expect("Tried to remove id that was not in list");
    event_handler.disconnect(id);
}

struct IdGen {
    count: Id,
}
impl IdGen {
    fn new() -> Self {
        IdGen { count: 0 }
    }
}
impl Iterator for IdGen {
    type Item = Id;

    fn next(&mut self) -> Option<Id> {
        if self.count != Id::max_value() {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

#[test]
fn id_gen_unique() {
    let mut id_gen = IdGen::new();
    let mut ids = Vec::new();
    let ids_count = 1000;
    for _ in 0..ids_count {
        ids.push(id_gen.next());
    }
    ids.sort();
    ids.dedup_by_key(|id| *id);
    assert_eq!(ids_count, ids.len());
}
<|endoftext|>


<commit_before>extern crate peg;
pub mod config_parser;

pub struct ConfigParser;

impl ConfigParser {

}
<commit_msg>started on the PEG config parser<commit_after>extern crate peg;
pub mod config_parser;

pub struct ConfigParser;

enum BindSym {
	Key(String),
	Symbols(String),
	Actions(Vec<String>),
}

enum Symbols {

}

enum Variables {

}

peg! parser(r#"
#[pub]

	"#)<|endoftext|>


<commit_before>//! Missing batteries for standard libraries.

use std::{cell::Cell, fmt};

/// Appends formatted string to a `String`.
#[macro_export]
macro_rules! format_to {
    (&buf:expr) => ();
    ($buf:expr, $lit:literal $($arg:tt)*) => {
        { use ::std::fmt::Write as _; let _ = ::std::write!($buf, $lit $($arg)*); }
    };
}

pub trait SepBy: Sized {
    /// Returns an `impl fmt::Display`, which joins elements via a separator.
    fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self>;
}

impl<I> SepBy for I
where
    I: Iterator,
    I::Item: fmt::Display,
{
    fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self> {
        SepByBuilder::new(sep, self)
    }
}

pub struct SepByBuilder<'a, I> {
    sep: &'a str,
    prefix: &'a str,
    suffix: &'a str,
    iter: Cell<Option<I>>,
}

impl<'a, I> SepByBuilder<'a, I> {
    fn new(sep: &'a str, iter: I) -> SepByBuilder<'a, I> {
        SepByBuilder { sep, prefix: "", suffix: "", iter: Cell::new(Some(iter)) }
    }

    pub fn prefix(mut self, prefix: &'a str) -> Self {
        self.prefix = prefix;
        self
    }

    pub fn suffix(mut self, suffix: &'a str) -> Self {
        self.suffix = suffix;
        self
    }

    /// Set both suffix and prefix.
    pub fn surround_with(self, prefix: &'a str, suffix: &'a str) -> Self {
        self.prefix(prefix).suffix(suffix)
    }
}

impl<I> fmt::Display for SepByBuilder<'_, I>
where
    I: Iterator,
    I::Item: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.prefix)?;
        let mut first = true;
        for item in self.iter.take().unwrap() {
            if !first {
                f.write_str(self.sep)?;
            }
            first = false;
            fmt::Display::fmt(&item, f)?;
        }
        f.write_str(self.suffix)?;
        Ok(())
    }
}
<commit_msg>Fix typo<commit_after>//! Missing batteries for standard libraries.

use std::{cell::Cell, fmt};

/// Appends formatted string to a `String`.
#[macro_export]
macro_rules! format_to {
    ($buf:expr) => ();
    ($buf:expr, $lit:literal $($arg:tt)*) => {
        { use ::std::fmt::Write as _; let _ = ::std::write!($buf, $lit $($arg)*); }
    };
}

pub trait SepBy: Sized {
    /// Returns an `impl fmt::Display`, which joins elements via a separator.
    fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self>;
}

impl<I> SepBy for I
where
    I: Iterator,
    I::Item: fmt::Display,
{
    fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self> {
        SepByBuilder::new(sep, self)
    }
}

pub struct SepByBuilder<'a, I> {
    sep: &'a str,
    prefix: &'a str,
    suffix: &'a str,
    iter: Cell<Option<I>>,
}

impl<'a, I> SepByBuilder<'a, I> {
    fn new(sep: &'a str, iter: I) -> SepByBuilder<'a, I> {
        SepByBuilder { sep, prefix: "", suffix: "", iter: Cell::new(Some(iter)) }
    }

    pub fn prefix(mut self, prefix: &'a str) -> Self {
        self.prefix = prefix;
        self
    }

    pub fn suffix(mut self, suffix: &'a str) -> Self {
        self.suffix = suffix;
        self
    }

    /// Set both suffix and prefix.
    pub fn surround_with(self, prefix: &'a str, suffix: &'a str) -> Self {
        self.prefix(prefix).suffix(suffix)
    }
}

impl<I> fmt::Display for SepByBuilder<'_, I>
where
    I: Iterator,
    I::Item: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.prefix)?;
        let mut first = true;
        for item in self.iter.take().unwrap() {
            if !first {
                f.write_str(self.sep)?;
            }
            first = false;
            fmt::Display::fmt(&item, f)?;
        }
        f.write_str(self.suffix)?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Module for implementing convert's ToTable.

use hlua::any::AnyLuaValue;
use hlua::any::AnyLuaValue::*;

/// Represents types which can be serialized into a Lua table (AnyLuaValue).
///
/// If you wish to continue using the object after serialization, consider
/// using `impl<a> ToTable for &'a T`, which will take an `&T` as its `self`.
pub trait ToTable {
    /// Write this value into an AnyLuaValue
    fn to_table(self) -> AnyLuaValue;
}

// Implementation for standard numeric types
macro_rules! numeric_impl {
    ($($typ:ty), +) => {
        $(impl ToTable for $typ {
            fn to_table(self) -> AnyLuaValue {
                LuaNumber(self as f64)
            }
        })+
    }
}

numeric_impl!(usize, isize);
numeric_impl!(i8, i16, i32);
numeric_impl!(u8, u16, u32);
numeric_impl!(f32, f64);

// Implementation for &Ts which use Copy syntax
impl<'a, T> ToTable for &'a T where T: Copy + ToTable {
    fn to_table(self) -> AnyLuaValue {
        self.clone().to_table()
    }
}

impl ToTable for bool {
    fn to_table(self) -> AnyLuaValue {
        LuaBoolean(self)
    }
}

impl ToTable for String {
    fn to_table(self) -> AnyLuaValue {
        LuaString(self)
    }
}

impl<T: ToTable> ToTable for Option<T> {
    fn to_table(self) -> AnyLuaValue {
        match self {
            Some(val) => val.to_table(),
            None => LuaNil
        }
    }
}

impl<T: ToTable> ToTable for Vec<T> {
    fn to_table(self) -> AnyLuaValue {
        LuaArray(self.into_iter().enumerate()
            .map(|(ix, val)| {
                (LuaNumber(ix as f64), val.to_table())
            }).collect())
    }
}

impl ToTable for () {
    fn to_table(self) -> AnyLuaValue {
        LuaNil
    }
}
<commit_msg>Remove ToTable for &T, add for HashTable<commit_after>//! Module for implementing convert's ToTable.

use hlua::any::AnyLuaValue;
use hlua::any::AnyLuaValue::*;

/// Represents types which can be serialized into a Lua table (AnyLuaValue).
///
/// If you wish to continue using the object after serialization, consider
/// using `impl<a> ToTable for &'a T`, which will take an `&T` as its `self`.
pub trait ToTable {
    /// Write this value into an AnyLuaValue
    fn to_table(self) -> AnyLuaValue;
}

// Implementation for standard numeric types
macro_rules! numeric_impl {
    ($($typ:ty), +) => {
        $(impl ToTable for $typ {
            fn to_table(self) -> AnyLuaValue {
                LuaNumber(self as f64)
            }
        })+
    }
}

numeric_impl!(usize, isize);
numeric_impl!(i8, i16, i32);
numeric_impl!(u8, u16, u32);
numeric_impl!(f32, f64);

impl ToTable for bool {
    fn to_table(self) -> AnyLuaValue {
        LuaBoolean(self)
    }
}

impl ToTable for String {
    fn to_table(self) -> AnyLuaValue {
        LuaString(self)
    }
}

impl<T: ToTable> ToTable for Option<T> {
    fn to_table(self) -> AnyLuaValue {
        match self {
            Some(val) => val.to_table(),
            None => LuaNil
        }
    }
}

impl<T: ToTable> ToTable for Vec<T> {
    fn to_table(self) -> AnyLuaValue {
        LuaArray(self.into_iter().enumerate()
            .map(|(ix, val)| {
                (LuaNumber(ix as f64), val.to_table())
            }).collect())
    }
}

use std::collections::HashMap;
use std::hash::Hash;
impl<K, V> ToTable for HashMap<K, V>
where K: Eq + Hash + ToTable, V: ToTable {
    fn to_table(self) -> AnyLuaValue {
        let mut table = Vec::with_capacity(self.capacity());
        for (key, value) in self.into_iter() {
            table.push((key.to_table(), value.to_table()));
        }
        LuaArray(table)
    }
}

impl ToTable for () {
    fn to_table(self) -> AnyLuaValue {
        LuaNil
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashSet, HashMap};
use log;
use cpp_data::{CppData, CppTypeKind, CppVisibility};
use caption_strategy::MethodCaptionStrategy;
use cpp_method::{CppMethod, CppMethodKind};
use cpp_ffi_data::{CppAndFfiMethod, c_base_name};
use utils::add_to_multihash;
use serializable::CppLibSpec;

struct CGenerator<'a> {
  abstract_classes: Vec<String>,
  cpp_data: &'a CppData,
  cpp_lib_spec: CppLibSpec,
}

#[derive(Debug, Clone)]
pub struct CppFfiHeaderData {
  pub include_file: String,
  pub include_file_base_name: String,
  pub methods: Vec<CppAndFfiMethod>,
}

pub struct CppAndFfiData {
  pub cpp_data: CppData,
  pub cpp_ffi_headers: Vec<CppFfiHeaderData>,
}

/// Runs FFI generator
pub fn run(cpp_data: &CppData, cpp_lib_spec: CppLibSpec) -> Vec<CppFfiHeaderData> {
  let abstract_classes = cpp_data.types
    .iter()
    .filter_map(|t| {
      if let CppTypeKind::Class { .. } = t.kind {
        if cpp_data.get_pure_virtual_methods(&t.name).len() > 0 {
          Some(t.name.clone())
        } else {
          None
        }
      } else {
        None
      }
    })
    .collect();
  log::info(format!("Abstract classes: {:?}", abstract_classes));
  let generator = CGenerator {
    cpp_data: cpp_data,
    abstract_classes: abstract_classes,
    cpp_lib_spec: cpp_lib_spec,
  };

  let mut c_headers = Vec::new();
  let mut include_name_list: Vec<_> = generator.cpp_data.all_include_files().into_iter().collect();
  include_name_list.sort();

  for include_file in &include_name_list {
    if let Some(ref include_file_blacklist) = generator.cpp_lib_spec.include_file_blacklist {
      if include_file_blacklist.iter().find(|x| x == &include_file).is_some() {
        log::info(format!("Skipping include file {}", include_file));
        continue;
      }
    }
    let mut include_file_base_name = include_file.clone();

    if let Some(index) = include_file_base_name.find(".") {
      include_file_base_name = include_file_base_name[0..index].to_string();
    }
    let methods = generator.process_methods(&include_file,
                                            &include_file_base_name,
                                            generator.cpp_data
                                              .methods
                                              .iter()
                                              .filter(|x| &x.include_file == include_file));
    c_headers.push(CppFfiHeaderData {
      include_file: include_file.clone(),
      include_file_base_name: include_file_base_name,
      methods: methods,
    });
  }
  c_headers
}

impl<'a> CGenerator<'a> {
  /// Returns false if the method is excluded from processing
  /// for some reason
  fn should_process_method(&self, method: &CppMethod) -> bool {
    let full_name = method.full_name();
    let short_text = method.short_text();
    let class_name = method.class_name().unwrap_or(&String::new()).clone();
    log::debug(format!("method name: {}", full_name));
    log::debug(format!(" short_text: {}", short_text));
    if let Some(ref ffi_methods_blacklist) = self.cpp_lib_spec.ffi_methods_blacklist {
      if ffi_methods_blacklist.iter()
        .find(|&x| x == &full_name || x == &short_text || x == &class_name)
        .is_some() {
        log::noisy(format!("Skipping blacklisted method: \n{}\n", method.short_text()));
        return false;
      }
    }
    if let Some(ref membership) = method.class_membership {
      if membership.kind == CppMethodKind::Constructor {
        let class_name = &membership.class_type.name;
        if self.abstract_classes.iter().find(|&x| x == class_name).is_some() {
          log::noisy(format!("Method is skipped:\n{}\nConstructors are not allowed for abstract \
                              classes.\n",
                             method.short_text()));
          return false;
        }
      }
      if membership.visibility == CppVisibility::Private {
        return false;
      }
      if membership.visibility == CppVisibility::Protected {
        log::noisy(format!("Skipping protected method: \n{}\n", method.short_text()));
        return false;
      }
      if membership.is_signal {
        log::warning(format!("Skipping signal: \n{}\n", method.short_text()));
        return false;
      }
    }
    if method.template_arguments.is_some() {
      // TODO: check findChildren and similar instantiated methods
      log::noisy(format!("Skipping template method: \n{}\n", method.short_text()));
      return false;
    }
    if method.all_involved_types()
      .iter()
      .find(|x| x.base.is_or_contains_template_parameter())
      .is_some() {
      log::noisy(format!("Skipping method containing template parameters: \n{}\n",
                         method.short_text()));
      return false;
    }
    true
  }

  /// Generates FFI wrappers for all specified methods,
  /// resolving all name conflicts using additional method captions.
  fn process_methods<'b, I: Iterator<Item = &'b CppMethod>>(&self,
                                                            include_file: &String,
                                                            include_file_base_name: &String,
                                                            methods: I)
                                                            -> Vec<CppAndFfiMethod> {
    log::info(format!("Generating C++ FFI methods for header: {}", include_file));
    let mut hash_name_to_methods: HashMap<String, Vec<_>> = HashMap::new();
    for ref method in methods {
      if !self.should_process_method(method) {
        continue;
      }
      match method.to_ffi_signatures() {
        Err(msg) => {
          log::warning(format!("Unable to produce C function for method:\n{}\nError:{}\n",
                               method.short_text(),
                               msg));
        }
        Ok(results) => {
          for result in results {
            match c_base_name(&result.cpp_method,
                              &result.allocation_place,
                              include_file_base_name) {
              Err(msg) => {
                log::warning(format!("Unable to produce C function for method:\n{}\nError:{}\n",
                                     method.short_text(),
                                     msg));
              }
              Ok(name) => {
                add_to_multihash(&mut hash_name_to_methods, &name, result);
              }
            }
          }
        }
      }
    }

    let mut processed_methods = Vec::new();
    for (key, mut values) in hash_name_to_methods.into_iter() {
      if values.len() == 1 {
        processed_methods.push(CppAndFfiMethod::new(values.remove(0), key.clone()));
        continue;
      }
      let mut found_strategy = None;
      for strategy in MethodCaptionStrategy::all() {
        let mut type_captions: HashSet<_> = HashSet::new();
        let mut ok = true;
        for value in &values {
          let caption = value.c_signature.caption(strategy.clone());
          if type_captions.contains(&caption) {
            ok = false;
            break;
          }
          type_captions.insert(caption);
        }
        if ok {
          found_strategy = Some(strategy);
          break;
        }
      }
      if let Some(strategy) = found_strategy {
        for x in values {
          let caption = x.c_signature.caption(strategy.clone());
          let final_name = if caption.is_empty() {
            key.clone()
          } else {
            format!("{}_{}", key, caption)
          };
          processed_methods.push(CppAndFfiMethod::new(x, final_name));
        }
      } else {
        log::error(format!("values dump: {:?}\n", values));
        log::error(format!("All type caption strategies have failed! Involved functions:"));
        for value in values {
          log::error(format!("  {}", value.cpp_method.short_text()));
        }
        panic!("all type caption strategies have failed");
      }
    }
    processed_methods.sort_by(|a, b| a.c_name.cmp(&b.c_name));
    processed_methods
  }
}
<commit_msg>add lib name prefix to C function names<commit_after>use std::collections::{HashSet, HashMap};
use log;
use cpp_data::{CppData, CppTypeKind, CppVisibility};
use caption_strategy::MethodCaptionStrategy;
use cpp_method::{CppMethod, CppMethodKind};
use cpp_ffi_data::{CppAndFfiMethod, c_base_name};
use utils::add_to_multihash;
use serializable::CppLibSpec;

struct CGenerator<'a> {
  abstract_classes: Vec<String>,
  cpp_data: &'a CppData,
  cpp_lib_spec: CppLibSpec,
}

#[derive(Debug, Clone)]
pub struct CppFfiHeaderData {
  pub include_file: String,
  pub include_file_base_name: String,
  pub methods: Vec<CppAndFfiMethod>,
}

pub struct CppAndFfiData {
  pub cpp_data: CppData,
  pub cpp_ffi_headers: Vec<CppFfiHeaderData>,
}

/// Runs FFI generator
pub fn run(cpp_data: &CppData, cpp_lib_spec: CppLibSpec) -> Vec<CppFfiHeaderData> {
  let abstract_classes = cpp_data.types
    .iter()
    .filter_map(|t| {
      if let CppTypeKind::Class { .. } = t.kind {
        if cpp_data.get_pure_virtual_methods(&t.name).len() > 0 {
          Some(t.name.clone())
        } else {
          None
        }
      } else {
        None
      }
    })
    .collect();
  log::info(format!("Abstract classes: {:?}", abstract_classes));
  let generator = CGenerator {
    cpp_data: cpp_data,
    abstract_classes: abstract_classes,
    cpp_lib_spec: cpp_lib_spec,
  };

  let mut c_headers = Vec::new();
  let mut include_name_list: Vec<_> = generator.cpp_data.all_include_files().into_iter().collect();
  include_name_list.sort();

  for include_file in &include_name_list {
    if let Some(ref include_file_blacklist) = generator.cpp_lib_spec.include_file_blacklist {
      if include_file_blacklist.iter().find(|x| x == &include_file).is_some() {
        log::info(format!("Skipping include file {}", include_file));
        continue;
      }
    }
    let mut include_file_base_name = include_file.clone();

    if let Some(index) = include_file_base_name.find(".") {
      include_file_base_name = include_file_base_name[0..index].to_string();
    }
    let methods = generator.process_methods(&include_file,
                                            &include_file_base_name,
                                            generator.cpp_data
                                              .methods
                                              .iter()
                                              .filter(|x| &x.include_file == include_file));
    c_headers.push(CppFfiHeaderData {
      include_file: include_file.clone(),
      include_file_base_name: include_file_base_name,
      methods: methods,
    });
  }
  c_headers
}

impl<'a> CGenerator<'a> {
  /// Returns false if the method is excluded from processing
  /// for some reason
  fn should_process_method(&self, method: &CppMethod) -> bool {
    let full_name = method.full_name();
    let short_text = method.short_text();
    let class_name = method.class_name().unwrap_or(&String::new()).clone();
    log::debug(format!("method name: {}", full_name));
    log::debug(format!(" short_text: {}", short_text));
    if let Some(ref ffi_methods_blacklist) = self.cpp_lib_spec.ffi_methods_blacklist {
      if ffi_methods_blacklist.iter()
        .find(|&x| x == &full_name || x == &short_text || x == &class_name)
        .is_some() {
        log::noisy(format!("Skipping blacklisted method: \n{}\n", method.short_text()));
        return false;
      }
    }
    if let Some(ref membership) = method.class_membership {
      if membership.kind == CppMethodKind::Constructor {
        let class_name = &membership.class_type.name;
        if self.abstract_classes.iter().find(|&x| x == class_name).is_some() {
          log::noisy(format!("Method is skipped:\n{}\nConstructors are not allowed for abstract \
                              classes.\n",
                             method.short_text()));
          return false;
        }
      }
      if membership.visibility == CppVisibility::Private {
        return false;
      }
      if membership.visibility == CppVisibility::Protected {
        log::noisy(format!("Skipping protected method: \n{}\n", method.short_text()));
        return false;
      }
      if membership.is_signal {
        log::warning(format!("Skipping signal: \n{}\n", method.short_text()));
        return false;
      }
    }
    if method.template_arguments.is_some() {
      // TODO: check findChildren and similar instantiated methods
      log::noisy(format!("Skipping template method: \n{}\n", method.short_text()));
      return false;
    }
    if method.all_involved_types()
      .iter()
      .find(|x| x.base.is_or_contains_template_parameter())
      .is_some() {
      log::noisy(format!("Skipping method containing template parameters: \n{}\n",
                         method.short_text()));
      return false;
    }
    true
  }

  /// Generates FFI wrappers for all specified methods,
  /// resolving all name conflicts using additional method captions.
  fn process_methods<'b, I: Iterator<Item = &'b CppMethod>>(&self,
                                                            include_file: &String,
                                                            include_file_base_name: &String,
                                                            methods: I)
                                                            -> Vec<CppAndFfiMethod> {
    log::info(format!("Generating C++ FFI methods for header: {}", include_file));
    let mut hash_name_to_methods: HashMap<String, Vec<_>> = HashMap::new();
    for ref method in methods {
      if !self.should_process_method(method) {
        continue;
      }
      match method.to_ffi_signatures() {
        Err(msg) => {
          log::warning(format!("Unable to produce C function for method:\n{}\nError:{}\n",
                               method.short_text(),
                               msg));
        }
        Ok(results) => {
          for result in results {
            match c_base_name(&result.cpp_method,
                              &result.allocation_place,
                              include_file_base_name) {
              Err(msg) => {
                log::warning(format!("Unable to produce C function for method:\n{}\nError:{}\n",
                                     method.short_text(),
                                     msg));
              }
              Ok(name) => {

                add_to_multihash(&mut hash_name_to_methods,
                                 &format!("{}_{}", &self.cpp_lib_spec.name, name),
                                 result);
              }
            }
          }
        }
      }
    }

    let mut processed_methods = Vec::new();
    for (key, mut values) in hash_name_to_methods.into_iter() {
      if values.len() == 1 {
        processed_methods.push(CppAndFfiMethod::new(values.remove(0), key.clone()));
        continue;
      }
      let mut found_strategy = None;
      for strategy in MethodCaptionStrategy::all() {
        let mut type_captions: HashSet<_> = HashSet::new();
        let mut ok = true;
        for value in &values {
          let caption = value.c_signature.caption(strategy.clone());
          if type_captions.contains(&caption) {
            ok = false;
            break;
          }
          type_captions.insert(caption);
        }
        if ok {
          found_strategy = Some(strategy);
          break;
        }
      }
      if let Some(strategy) = found_strategy {
        for x in values {
          let caption = x.c_signature.caption(strategy.clone());
          let final_name = if caption.is_empty() {
            key.clone()
          } else {
            format!("{}_{}", key, caption)
          };
          processed_methods.push(CppAndFfiMethod::new(x, final_name));
        }
      } else {
        log::error(format!("values dump: {:?}\n", values));
        log::error(format!("All type caption strategies have failed! Involved functions:"));
        for value in values {
          log::error(format!("  {}", value.cpp_method.short_text()));
        }
        panic!("all type caption strategies have failed");
      }
    }
    processed_methods.sort_by(|a, b| a.c_name.cmp(&b.c_name));
    processed_methods
  }
}
<|endoftext|>


<commit_before>// Copyright 2013 The GLFW-RS Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

extern crate native;
extern crate glfw;

use glfw::Context;

#[start]
fn start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, main)
}

fn main() {
    let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    glfw.window_hint(glfw::Visible(true));

    let (window, _) = glfw.create_window(640, 480, "Defaults", glfw::Windowed)
        .expect("Failed to create GLFW window.");

    window.make_current();

    let (width, height) = window.get_size();
    println!("window size: ({}, {})", width, height);

    println!("Context version: {:s}",         window.get_context_version().to_str());
    println!("OpenGL forward compatible: {}", window.is_opengl_forward_compat());
    println!("OpenGL debug context: {}",      window.is_opengl_debug_context());
    println!("OpenGL profile: {}",            window.get_opengl_profile());

    let gl_params = [
        (gl::RED_BITS,          None,   "red bits"          ),
        (gl::GREEN_BITS,        None,   "green bits"        ),
        (gl::BLUE_BITS,         None,   "blue bits"         ),
        (gl::ALPHA_BITS,        None,   "alpha bits"        ),
        (gl::DEPTH_BITS,        None,   "depth bits"        ),
        (gl::STENCIL_BITS,      None,   "stencil bits"      ),
        (gl::ACCUM_RED_BITS,    None,   "accum red bits"    ),
        (gl::ACCUM_GREEN_BITS,  None,   "accum green bits"  ),
        (gl::ACCUM_BLUE_BITS,   None,   "accum blue bits"   ),
        (gl::ACCUM_ALPHA_BITS,  None,   "accum alpha bits"  ),
        (gl::STEREO,            None,   "stereo"            ),
        (gl::SAMPLES_ARB,       Some("GL_ARB_multisample"), "FSAA samples" ),
    ];

    for &(param, ext, name) in gl_params.iter() {
        if ext.map_or(true, |s| {
            glfw.extension_supported(s)
        }) {
            let value = 0;
            unsafe { gl::GetIntegerv(param, &value) };
            println!("OpenGL {:s}: {}", name, value);
        };
    }
}

mod gl {
    extern crate libc;

    #[cfg(target_os = "macos")]
    #[link(name="OpenGL", kind="framework")]
    extern { }

    #[cfg(target_os = "linux")]
    #[link(name="GL")]
    extern { }

    pub type GLenum = libc::c_uint;
    pub type GLint  = libc::c_int;

    pub static RED_BITS              : GLenum = 0x0D52;
    pub static GREEN_BITS            : GLenum = 0x0D53;
    pub static BLUE_BITS             : GLenum = 0x0D54;
    pub static ALPHA_BITS            : GLenum = 0x0D55;
    pub static DEPTH_BITS            : GLenum = 0x0D56;
    pub static STENCIL_BITS          : GLenum = 0x0D57;
    pub static ACCUM_RED_BITS        : GLenum = 0x0D58;
    pub static ACCUM_GREEN_BITS      : GLenum = 0x0D59;
    pub static ACCUM_BLUE_BITS       : GLenum = 0x0D5A;
    pub static ACCUM_ALPHA_BITS      : GLenum = 0x0D5B;
    pub static STEREO                : GLenum = 0x0C33;
    pub static SAMPLES_ARB           : GLenum = 0x80A9;

    #[inline(never)]
    pub unsafe fn GetIntegerv(pname: GLenum, params: *GLint) {
        glGetIntegerv(pname, params)
    }

    extern "C" {
        fn glGetIntegerv(pname: GLenum, params: *GLint);
    }
}
<commit_msg>Fix snake case compiler warning<commit_after>// Copyright 2013 The GLFW-RS Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

extern crate native;
extern crate glfw;

use glfw::Context;

#[start]
fn start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, main)
}

fn main() {
    let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    glfw.window_hint(glfw::Visible(true));

    let (window, _) = glfw.create_window(640, 480, "Defaults", glfw::Windowed)
        .expect("Failed to create GLFW window.");

    window.make_current();

    let (width, height) = window.get_size();
    println!("window size: ({}, {})", width, height);

    println!("Context version: {:s}",         window.get_context_version().to_str());
    println!("OpenGL forward compatible: {}", window.is_opengl_forward_compat());
    println!("OpenGL debug context: {}",      window.is_opengl_debug_context());
    println!("OpenGL profile: {}",            window.get_opengl_profile());

    let gl_params = [
        (gl::RED_BITS,          None,   "red bits"          ),
        (gl::GREEN_BITS,        None,   "green bits"        ),
        (gl::BLUE_BITS,         None,   "blue bits"         ),
        (gl::ALPHA_BITS,        None,   "alpha bits"        ),
        (gl::DEPTH_BITS,        None,   "depth bits"        ),
        (gl::STENCIL_BITS,      None,   "stencil bits"      ),
        (gl::ACCUM_RED_BITS,    None,   "accum red bits"    ),
        (gl::ACCUM_GREEN_BITS,  None,   "accum green bits"  ),
        (gl::ACCUM_BLUE_BITS,   None,   "accum blue bits"   ),
        (gl::ACCUM_ALPHA_BITS,  None,   "accum alpha bits"  ),
        (gl::STEREO,            None,   "stereo"            ),
        (gl::SAMPLES_ARB,       Some("GL_ARB_multisample"), "FSAA samples" ),
    ];

    for &(param, ext, name) in gl_params.iter() {
        if ext.map_or(true, |s| {
            glfw.extension_supported(s)
        }) {
            let value = 0;
            unsafe { gl::GetIntegerv(param, &value) };
            println!("OpenGL {:s}: {}", name, value);
        };
    }
}

mod gl {
    extern crate libc;

    #[cfg(target_os = "macos")]
    #[link(name="OpenGL", kind="framework")]
    extern { }

    #[cfg(target_os = "linux")]
    #[link(name="GL")]
    extern { }

    pub type GLenum = libc::c_uint;
    pub type GLint  = libc::c_int;

    pub static RED_BITS              : GLenum = 0x0D52;
    pub static GREEN_BITS            : GLenum = 0x0D53;
    pub static BLUE_BITS             : GLenum = 0x0D54;
    pub static ALPHA_BITS            : GLenum = 0x0D55;
    pub static DEPTH_BITS            : GLenum = 0x0D56;
    pub static STENCIL_BITS          : GLenum = 0x0D57;
    pub static ACCUM_RED_BITS        : GLenum = 0x0D58;
    pub static ACCUM_GREEN_BITS      : GLenum = 0x0D59;
    pub static ACCUM_BLUE_BITS       : GLenum = 0x0D5A;
    pub static ACCUM_ALPHA_BITS      : GLenum = 0x0D5B;
    pub static STEREO                : GLenum = 0x0C33;
    pub static SAMPLES_ARB           : GLenum = 0x80A9;

    #[inline(never)]
    #[allow(non_snake_case_functions)]
    pub unsafe fn GetIntegerv(pname: GLenum, params: *GLint) {
        glGetIntegerv(pname, params)
    }

    extern "C" {
        fn glGetIntegerv(pname: GLenum, params: *GLint);
    }
}
<|endoftext|>


<commit_before>//! Routes are used for ratelimiting. These are to differentiate between the
//! different _types_ of routes - such as getting the current user's channels -
//! for the most part, with the exception being major parameters.
//!
//! [Taken from] the Discord docs, major parameters are:
//!
//! > Additionally, rate limits take into account major parameters in the URL.
//! > For example, `/channels/:channel_id` and
//! > `/channels/:channel_id/messages/:message_id` both take `channel_id` into
//! > account when generating rate limits since it's the major parameter. The
//! only current major parameters are `channel_id`, `guild_id` and `webhook_id`.
//!
//! This results in the two URIs of `GET /channels/4/messages/7` and
//! `GET /channels/5/messages/8` being rate limited _separately_. However, the
//! two URIs of `GET /channels/10/messages/11` and
//! `GET /channels/10/messages/12` will count towards the "same ratelimit", as
//! the major parameter - `10` is equivalent in both URIs' format.
//!
//! # Examples
//!
//! First: taking the first two URIs - `GET /channels/4/messages/7` and
//! `GET /channels/5/messages/8` - and assuming both buckets have a `limit` of
//! `10`, requesting the first URI will result in the response containing a
//! `remaining` of `9`. Immediately after - prior to buckets resetting -
//! performing a request to the _second_ URI will also contain a `remaining` of
//! `9` in the response, as the major parameter - `channel_id` - is different
//! in the two requests (`4` and `5`).
//!
//! Second: take for example the last two URIs. Assuming the bucket's `limit` is
//! `10`, requesting the first URI will return a `remaining` of `9` in the
//! response. Immediately after - prior to buckets resetting - performing a
//! request to the _second_ URI will return a `remaining` of `8` in the
//! response, as the major parameter - `channel_id` - is equivalent for the two
//! requests (`10`).
//!
//! Major parameters are why some variants (i.e. all of the channel/guild
//! variants) have an associated u64 as data. This is the Id of the parameter,
//! differentiating between different ratelimits.
//!
//! [Taken from]: https://discordapp.com/developers/docs/topics/rate-limits#rate-limits
pub use super::routing::Route;

use chrono::{DateTime, Utc};
use reqwest::{
    Response,
    header::HeaderMap as Headers,
    StatusCode,
};
use crate::internal::prelude::*;
use parking_lot::Mutex;
use std::{
    sync::Arc,
    time::Duration,
    str,
    thread,
    i64,
};
use super::{Http, HttpError, Request};
use log::debug;

/// Refer to [`offset`].
///
/// [`offset`]: fn.offset.html
static mut OFFSET: Option<i64> = None;

pub(super) fn perform(http: &Http, req: Request<'_>) -> Result<Response> {
    loop {
        // This will block if another thread is trying to send
        // an HTTP-request already (due to receiving an x-ratelimit-global).
        let _ = http.limiter.lock();

        // Destructure the tuple instead of retrieving the third value to
        // take advantage of the type system. If `RouteInfo::deconstruct`
        // returns a different number of tuple elements in the future, directly
        // accessing a certain index (e.g. `req.route.deconstruct().1`) would
        // mean this code would not indicate it might need to be updated for the
        // new tuple element amount.
        //
        // This isn't normally important, but might be for ratelimiting.
        let (_, route, _) = req.route.deconstruct();

        // Perform pre-checking here:
        //
        // - get the route's relevant rate
        // - sleep if that route's already rate-limited until the end of the
        //   'reset' time;
        // - get the global rate;
        // - sleep if there is 0 remaining
        // - then, perform the request
        let bucket = Arc::clone(http.routes
            .lock()
            .entry(route)
            .or_insert_with(|| {
                Arc::new(Mutex::new(RateLimit {
                    limit: i64::MAX,
                    remaining: i64::MAX,
                    reset: i64::MAX,
                }))
            }));

        let mut lock = bucket.lock();
        lock.pre_hook(&route);

        let response = http.retry(&req)?;

        // Check if an offset has been calculated yet to determine the time
        // difference from Discord can the client.
        //
        // Refer to the documentation for `OFFSET` for more information.
        //
        // This should probably only be a one-time check, although we may want
        // to choose to check this often in the future.
        if unsafe { OFFSET }.is_none() {
            calculate_offset(&response.headers().get("date").and_then(|d| Some(d.as_bytes())));
        }

        // Check if the request got ratelimited by checking for status 429,
        // and if so, sleep for the value of the header 'retry-after' -
        // which is in milliseconds - and then `continue` to try again
        //
        // If it didn't ratelimit, subtract one from the RateLimit's
        // 'remaining'
        //
        // Update the 'reset' with the value of the 'x-ratelimit-reset'
        // header
        //
        // It _may_ be possible for the limit to be raised at any time,
        // so check if it did from the value of the 'x-ratelimit-limit'
        // header. If the limit was 5 and is now 7, add 2 to the 'remaining'
        if route == Route::None {
            return Ok(response);
        } else {
            let redo = if response.headers().get("x-ratelimit-global").is_some() {
                let _ = http.limiter.lock();

                Ok(
                    if let Some(retry_after) = parse_header(&response.headers(), "retry-after")? {
                        debug!("Ratelimited on route {:?} for {:?}ms", route, retry_after);
                        thread::sleep(Duration::from_millis(retry_after as u64));

                        true
                    } else {
                        false
                    },
                )
            } else {
                lock.post_hook(&response, &route)
            };

            if !redo.unwrap_or(true) {
                return Ok(response);
            }
        }
    }
}

/// A set of data containing information about the ratelimits for a particular
/// [`Route`], which is stored in [`Http`].
///
/// See the [Discord docs] on ratelimits for more information.
///
/// **Note**: You should _not_ mutate any of the fields, as this can help cause
/// 429s.
///
/// [`Http`]: ../raw/struct.Http.html#structfield.routes
/// [`Route`]: ../routing/enum.Route.html
/// [Discord docs]: https://discordapp.com/developers/docs/topics/rate-limits
#[derive(Clone, Debug, Default)]
pub struct RateLimit {
    /// The total number of requests that can be made in a period of time.
    pub limit: i64,
    /// The number of requests remaining in the period of time.
    pub remaining: i64,
    /// When the interval resets and the the [`limit`] resets to the value of
    /// [`remaining`].
    ///
    /// [`limit`]: #structfield.limit
    /// [`remaining`]: #structfield.remaining
    pub reset: i64,
}

impl RateLimit {
    pub(crate) fn pre_hook(&mut self, route: &Route) {
        if self.limit == 0 {
            return;
        }

        let offset = unsafe { OFFSET }.unwrap_or(0);
        let now = Utc::now().timestamp();
        let current_time = now - offset;

        // The reset was in the past, so we're probably good.
        if current_time > self.reset {
            self.remaining = self.limit;

            return;
        }

        let diff = (self.reset - current_time) as u64;

        if self.remaining == 0 {
            let delay = (diff * 1000) + 500;

            debug!(
                "Pre-emptive ratelimit on route {:?} for {:?}ms",
                route,
                delay
            );
            thread::sleep(Duration::from_millis(delay));

            return;
        }

        self.remaining -= 1;
    }

    pub(crate) fn post_hook(&mut self, response: &Response, route: &Route) -> Result<bool> {
        if let Some(limit) = parse_header(&response.headers(), "x-ratelimit-limit")? {
            self.limit = limit;
        }

        if let Some(remaining) = parse_header(&response.headers(), "x-ratelimit-remaining")? {
            self.remaining = remaining;
        }

        if let Some(reset) = parse_header(&response.headers(), "x-ratelimit-reset")? {
            self.reset = reset;
        }

        Ok(if response.status() != StatusCode::TOO_MANY_REQUESTS {
            false
        } else if let Some(retry_after) = parse_header(&response.headers(), "retry-after")? {
            debug!("Ratelimited on route {:?} for {:?}ms", route, retry_after);
            thread::sleep(Duration::from_millis(retry_after as u64));

            true
        } else {
            false
        })
    }
}

/// The calculated offset of the time difference between Discord and the client
/// in seconds.
///
/// This does not have millisecond precision as calculating that isn't
/// realistic.
///
/// This is used in ratelimiting to help determine how long to wait for
/// pre-emptive ratelimits. For example, if the client is 2 seconds ahead, then
/// the client would think the ratelimit is over 2 seconds before it actually is
/// and would then send off queued requests. Using an offset, we can know that
/// there's actually still 2 seconds left (+/- some milliseconds).
///
/// This isn't a definitive solution to fix all problems, but it can help with
/// some precision gains.
///
/// This will return `None` if an HTTP request hasn't been made, meaning that
/// no offset could have been calculated.
pub fn offset() -> Option<i64> {
    unsafe { OFFSET }
}

fn calculate_offset(header: &Option<&[u8]>) {
    // Get the current time as soon as possible.
    let now = Utc::now().timestamp();

    let header = header.and_then(|x| str::from_utf8(x).ok());

    if let Some(header) = header {
        // Replace the `GMT` timezone with an offset, and then parse it
        // into a chrono DateTime. If it parses correctly, calculate the
        // diff and then set it as the offset.
        let s = header.replace("GMT", "+0000");

        let parsed = DateTime::parse_from_str(&s, "%a, %d %b %Y %T %z");

        if let Ok(parsed) = parsed {
            let offset = parsed.timestamp();

            let diff = offset - now;

            unsafe {
                OFFSET = Some(diff);

                debug!("[ratelimiting] Set the ratelimit offset to {}", diff);
            }
        }
    }

}

fn parse_header(headers: &Headers, header: &str) -> Result<Option<i64>> {
    headers.get(header).map_or(Ok(None), |header| {
        str::from_utf8(&header.as_bytes())
            .map_err(|_| Error::from(HttpError::RateLimitUtf8))
            .and_then(|v| {
                v.parse::<i64>()
                    .map(Some)
                    .map_err(|_| Error::from(HttpError::RateLimitI64))
            })
    })
}
<commit_msg>Stop adding 500 milliseconds of delay<commit_after>//! Routes are used for ratelimiting. These are to differentiate between the
//! different _types_ of routes - such as getting the current user's channels -
//! for the most part, with the exception being major parameters.
//!
//! [Taken from] the Discord docs, major parameters are:
//!
//! > Additionally, rate limits take into account major parameters in the URL.
//! > For example, `/channels/:channel_id` and
//! > `/channels/:channel_id/messages/:message_id` both take `channel_id` into
//! > account when generating rate limits since it's the major parameter. The
//! only current major parameters are `channel_id`, `guild_id` and `webhook_id`.
//!
//! This results in the two URIs of `GET /channels/4/messages/7` and
//! `GET /channels/5/messages/8` being rate limited _separately_. However, the
//! two URIs of `GET /channels/10/messages/11` and
//! `GET /channels/10/messages/12` will count towards the "same ratelimit", as
//! the major parameter - `10` is equivalent in both URIs' format.
//!
//! # Examples
//!
//! First: taking the first two URIs - `GET /channels/4/messages/7` and
//! `GET /channels/5/messages/8` - and assuming both buckets have a `limit` of
//! `10`, requesting the first URI will result in the response containing a
//! `remaining` of `9`. Immediately after - prior to buckets resetting -
//! performing a request to the _second_ URI will also contain a `remaining` of
//! `9` in the response, as the major parameter - `channel_id` - is different
//! in the two requests (`4` and `5`).
//!
//! Second: take for example the last two URIs. Assuming the bucket's `limit` is
//! `10`, requesting the first URI will return a `remaining` of `9` in the
//! response. Immediately after - prior to buckets resetting - performing a
//! request to the _second_ URI will return a `remaining` of `8` in the
//! response, as the major parameter - `channel_id` - is equivalent for the two
//! requests (`10`).
//!
//! Major parameters are why some variants (i.e. all of the channel/guild
//! variants) have an associated u64 as data. This is the Id of the parameter,
//! differentiating between different ratelimits.
//!
//! [Taken from]: https://discordapp.com/developers/docs/topics/rate-limits#rate-limits
pub use super::routing::Route;

use chrono::{DateTime, Utc};
use reqwest::{
    Response,
    header::HeaderMap as Headers,
    StatusCode,
};
use crate::internal::prelude::*;
use parking_lot::Mutex;
use std::{
    sync::Arc,
    time::Duration,
    str,
    thread,
    i64,
};
use super::{Http, HttpError, Request};
use log::debug;

/// Refer to [`offset`].
///
/// [`offset`]: fn.offset.html
static mut OFFSET: Option<i64> = None;

pub(super) fn perform(http: &Http, req: Request<'_>) -> Result<Response> {
    loop {
        // This will block if another thread is trying to send
        // an HTTP-request already (due to receiving an x-ratelimit-global).
        let _ = http.limiter.lock();

        // Destructure the tuple instead of retrieving the third value to
        // take advantage of the type system. If `RouteInfo::deconstruct`
        // returns a different number of tuple elements in the future, directly
        // accessing a certain index (e.g. `req.route.deconstruct().1`) would
        // mean this code would not indicate it might need to be updated for the
        // new tuple element amount.
        //
        // This isn't normally important, but might be for ratelimiting.
        let (_, route, _) = req.route.deconstruct();

        // Perform pre-checking here:
        //
        // - get the route's relevant rate
        // - sleep if that route's already rate-limited until the end of the
        //   'reset' time;
        // - get the global rate;
        // - sleep if there is 0 remaining
        // - then, perform the request
        let bucket = Arc::clone(http.routes
            .lock()
            .entry(route)
            .or_insert_with(|| {
                Arc::new(Mutex::new(RateLimit {
                    limit: i64::MAX,
                    remaining: i64::MAX,
                    reset: i64::MAX,
                }))
            }));

        let mut lock = bucket.lock();
        lock.pre_hook(&route);

        let response = http.retry(&req)?;

        // Check if an offset has been calculated yet to determine the time
        // difference from Discord can the client.
        //
        // Refer to the documentation for `OFFSET` for more information.
        //
        // This should probably only be a one-time check, although we may want
        // to choose to check this often in the future.
        if unsafe { OFFSET }.is_none() {
            calculate_offset(&response.headers().get("date").and_then(|d| Some(d.as_bytes())));
        }

        // Check if the request got ratelimited by checking for status 429,
        // and if so, sleep for the value of the header 'retry-after' -
        // which is in milliseconds - and then `continue` to try again
        //
        // If it didn't ratelimit, subtract one from the RateLimit's
        // 'remaining'
        //
        // Update the 'reset' with the value of the 'x-ratelimit-reset'
        // header
        //
        // It _may_ be possible for the limit to be raised at any time,
        // so check if it did from the value of the 'x-ratelimit-limit'
        // header. If the limit was 5 and is now 7, add 2 to the 'remaining'
        if route == Route::None {
            return Ok(response);
        } else {
            let redo = if response.headers().get("x-ratelimit-global").is_some() {
                let _ = http.limiter.lock();

                Ok(
                    if let Some(retry_after) = parse_header(&response.headers(), "retry-after")? {
                        debug!("Ratelimited on route {:?} for {:?}ms", route, retry_after);
                        thread::sleep(Duration::from_millis(retry_after as u64));

                        true
                    } else {
                        false
                    },
                )
            } else {
                lock.post_hook(&response, &route)
            };

            if !redo.unwrap_or(true) {
                return Ok(response);
            }
        }
    }
}

/// A set of data containing information about the ratelimits for a particular
/// [`Route`], which is stored in [`Http`].
///
/// See the [Discord docs] on ratelimits for more information.
///
/// **Note**: You should _not_ mutate any of the fields, as this can help cause
/// 429s.
///
/// [`Http`]: ../raw/struct.Http.html#structfield.routes
/// [`Route`]: ../routing/enum.Route.html
/// [Discord docs]: https://discordapp.com/developers/docs/topics/rate-limits
#[derive(Clone, Debug, Default)]
pub struct RateLimit {
    /// The total number of requests that can be made in a period of time.
    pub limit: i64,
    /// The number of requests remaining in the period of time.
    pub remaining: i64,
    /// When the interval resets and the the [`limit`] resets to the value of
    /// [`remaining`].
    ///
    /// [`limit`]: #structfield.limit
    /// [`remaining`]: #structfield.remaining
    pub reset: i64,
}

impl RateLimit {
    pub(crate) fn pre_hook(&mut self, route: &Route) {
        if self.limit == 0 {
            return;
        }

        let offset = unsafe { OFFSET }.unwrap_or(0);
        let now = Utc::now().timestamp();
        let current_time = now - offset;

        // The reset was in the past, so we're probably good.
        if current_time > self.reset {
            self.remaining = self.limit;

            return;
        }

        let diff = (self.reset - current_time) as u64;

        if self.remaining == 0 {
            let delay = diff * 1000;

            debug!(
                "Pre-emptive ratelimit on route {:?} for {:?}ms",
                route,
                delay
            );

            thread::sleep(Duration::from_millis(delay));

            return;
        }

        self.remaining -= 1;
    }

    pub(crate) fn post_hook(&mut self, response: &Response, route: &Route) -> Result<bool> {
        if let Some(limit) = parse_header(&response.headers(), "x-ratelimit-limit")? {
            self.limit = limit;
        }

        if let Some(remaining) = parse_header(&response.headers(), "x-ratelimit-remaining")? {
            self.remaining = remaining;
        }

        if let Some(reset) = parse_header(&response.headers(), "x-ratelimit-reset")? {
            self.reset = reset;
        }

        Ok(if response.status() != StatusCode::TOO_MANY_REQUESTS {
            false
        } else if let Some(retry_after) = parse_header(&response.headers(), "retry-after")? {
            debug!("Ratelimited on route {:?} for {:?}ms", route, retry_after);
            thread::sleep(Duration::from_millis(retry_after as u64));

            true
        } else {
            false
        })
    }
}

/// The calculated offset of the time difference between Discord and the client
/// in seconds.
///
/// This does not have millisecond precision as calculating that isn't
/// realistic.
///
/// This is used in ratelimiting to help determine how long to wait for
/// pre-emptive ratelimits. For example, if the client is 2 seconds ahead, then
/// the client would think the ratelimit is over 2 seconds before it actually is
/// and would then send off queued requests. Using an offset, we can know that
/// there's actually still 2 seconds left (+/- some milliseconds).
///
/// This isn't a definitive solution to fix all problems, but it can help with
/// some precision gains.
///
/// This will return `None` if an HTTP request hasn't been made, meaning that
/// no offset could have been calculated.
pub fn offset() -> Option<i64> {
    unsafe { OFFSET }
}

fn calculate_offset(header: &Option<&[u8]>) {
    // Get the current time as soon as possible.
    let now = Utc::now().timestamp();

    let header = header.and_then(|x| str::from_utf8(x).ok());

    if let Some(header) = header {
        // Replace the `GMT` timezone with an offset, and then parse it
        // into a chrono DateTime. If it parses correctly, calculate the
        // diff and then set it as the offset.
        let s = header.replace("GMT", "+0000");

        let parsed = DateTime::parse_from_str(&s, "%a, %d %b %Y %T %z");

        if let Ok(parsed) = parsed {
            let offset = parsed.timestamp();

            let diff = offset - now;

            unsafe {
                OFFSET = Some(diff);

                debug!("[ratelimiting] Set the ratelimit offset to {}", diff);
            }
        }
    }

}

fn parse_header(headers: &Headers, header: &str) -> Result<Option<i64>> {
    headers.get(header).map_or(Ok(None), |header| {
        str::from_utf8(&header.as_bytes())
            .map_err(|_| Error::from(HttpError::RateLimitUtf8))
            .and_then(|v| {
                v.parse::<i64>()
                    .map(Some)
                    .map_err(|_| Error::from(HttpError::RateLimitI64))
            })
    })
}
<|endoftext|>