<commit_before>pub mod conf;
pub mod handler;
pub mod handler_paths;
pub mod req;
pub mod resp;
pub mod server_conn;
pub mod server_tls;

use std::collections::HashMap;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;

use tls_api;

use tokio_core::reactor;

use futures::future;
use futures::future::join_all;
use futures::future::Future;
use futures::stream;
use futures::stream::Stream;
use futures::sync::oneshot;

use futures_cpupool;

use exec::CpuPoolOption;

use error::Error;
use result::Result;

use solicit_async::*;

use futures_misc::*;

use tls_api::TlsAcceptor;
use tls_api_stub;

use super::common::*;

use socket::AnySocketAddr;
use socket::ToSocketListener;
use socket::ToTokioListener;

pub use self::server_tls::ServerTlsOption;
pub use server::conf::ServerConf;
use server::handler::ServerHandler;
use server::handler_paths::ServerHandlerPaths;
pub use server::server_conn::ServerConn;

pub struct ServerBuilder<A: tls_api::TlsAcceptor = tls_api_stub::TlsAcceptor> {
    pub conf: ServerConf,
    pub cpu_pool: CpuPoolOption,
    pub tls: ServerTlsOption<A>,
    pub addr: Option<AnySocketAddr>,
    /// Event loop to spawn server.
    /// If not specified, builder will create new event loop in a new thread.
    pub event_loop: Option<reactor::Remote>,
    pub service: ServerHandlerPaths,
}

impl ServerBuilder<tls_api_stub::TlsAcceptor> {
    /// New server builder with defaults.
    ///
    /// Port must be set, other properties are optional.
    pub fn new_plain() -> ServerBuilder<tls_api_stub::TlsAcceptor> {
        ServerBuilder::new()
    }
}

#[cfg(unix)]
impl ServerBuilder<tls_api_stub::TlsAcceptor> {
    /// New unix domain socket server with defaults
    ///
    /// Addr must be set, other properties are optional.
    pub fn new_plain_unix() -> ServerBuilder<tls_api_stub::TlsAcceptor> {
        ServerBuilder::<tls_api_stub::TlsAcceptor>::new()
    }
}

impl<A: tls_api::TlsAcceptor> ServerBuilder<A> {
    /// Set port server listens on.
    /// Can be zero to bind on any available port,
    /// which can be later obtained by `Server::local_addr`.
    pub fn set_port(&mut self, port: u16) {
        self.set_addr(("::", port)).expect("set_addr");
    }

    /// Set port server listens on.
    pub fn set_addr<S: ToSocketAddrs>(&mut self, addr: S) -> Result<()> {
        let addrs: Vec<_> = addr.to_socket_addrs()?.collect();
        if addrs.is_empty() {
            return Err(Error::Other("addr is resolved to empty list"));
        } else if addrs.len() > 1 {
            return Err(Error::Other("addr is resolved to more than one addr"));
        }
        self.addr = Some(AnySocketAddr::Inet(addrs.into_iter().next().unwrap()));
        Ok(())
    }
}

#[cfg(unix)]
impl<A: tls_api::TlsAcceptor> ServerBuilder<A> {
    // Set name of unix domain socket
    pub fn set_unix_addr(&mut self, addr: String) -> Result<()> {
        self.addr = Some(AnySocketAddr::Unix(addr));
        Ok(())
    }
}

impl<A: tls_api::TlsAcceptor> ServerBuilder<A> {
    /// New server builder with defaults.
    ///
    /// To call this function `ServerBuilder` must be parameterized with TLS acceptor.
    /// If TLS is not needed, `ServerBuilder::new_plain` function can be used.
    ///
    /// Port must be set, other properties are optional.
    pub fn new() -> ServerBuilder<A> {
        ServerBuilder {
            conf: ServerConf::new(),
            cpu_pool: CpuPoolOption::SingleThread,
            tls: ServerTlsOption::Plain,
            addr: None,
            event_loop: None,
            service: ServerHandlerPaths::new(),
        }
    }

    /// Create a CPU pool, and use it in HTTP server
    pub fn set_cpu_pool_threads(&mut self, threads: usize) {
        let cpu_pool = futures_cpupool::Builder::new()
            .pool_size(threads)
            .name_prefix("httpbis-server-")
            .create();
        self.cpu_pool = CpuPoolOption::CpuPool(cpu_pool);
    }

    pub fn set_tls(&mut self, acceptor: A) {
        self.tls = ServerTlsOption::Tls(Arc::new(acceptor));
    }

    pub fn build(self) -> Result<Server> {
        let (alive_tx, alive_rx) = mpsc::channel();

        let state: Arc<Mutex<ServerState>> = Default::default();

        let state_copy = state.clone();

        let (shutdown_signal, shutdown_future) = shutdown_signal();

        // TODO: why done_tx is unused?
        let (_done_tx, done_rx) = oneshot::channel();

        let listen = self.addr.unwrap().to_listener(&self.conf);

        let local_addr = listen.local_addr().unwrap();
        //let local_addr = local_addr.downcast_ref::<T>().expect("downcast socket_addr").clone();

        let join = if let Some(remote) = self.event_loop {
            let tls = self.tls;
            let cpu_pool = self.cpu_pool;
            let conf = self.conf;
            let service = self.service;
            remote.spawn(move |handle| {
                drop(spawn_server_event_loop(
                    handle.clone(),
                    state_copy,
                    tls,
                    listen,
                    cpu_pool,
                    shutdown_future,
                    conf,
                    service,
                    alive_tx,
                ));
                future::finished(())
            });
            Completion::Rx(done_rx)
        } else {
            let tls = self.tls;
            let cpu_pool = self.cpu_pool;
            let conf = self.conf;
            let service = self.service;
            let join_handle = thread::Builder::new()
                .name(
                    conf.thread_name
                        .clone()
                        .unwrap_or_else(|| "http2-server-loop".to_owned())
                        .to_string(),
                ).spawn(move || {
                    let mut lp = reactor::Core::new().expect("http2server");
                    let done_rx = spawn_server_event_loop(
                        lp.handle(),
                        state_copy,
                        tls,
                        listen,
                        cpu_pool,
                        shutdown_future,
                        conf,
                        service,
                        alive_tx,
                    );
                    drop(lp.run(done_rx));
                })?;
            Completion::Thread(join_handle)
        };

        Ok(Server {
            state: state,
            shutdown: shutdown_signal,
            local_addr: local_addr,
            join: Some(join),
            alive_rx: alive_rx,
        })
    }
}

enum Completion {
    Thread(thread::JoinHandle<()>),
    Rx(oneshot::Receiver<()>),
}

pub struct Server {
    state: Arc<Mutex<ServerState>>,
    local_addr: AnySocketAddr,
    shutdown: ShutdownSignal,
    alive_rx: mpsc::Receiver<()>,
    join: Option<Completion>,
}

#[derive(Default)]
struct ServerState {
    last_conn_id: u64,
    conns: HashMap<u64, ServerConn>,
}

impl ServerState {
    fn snapshot(&self) -> HttpFutureSend<ServerStateSnapshot> {
        let futures: Vec<_> = self
            .conns
            .iter()
            .map(|(&id, conn)| conn.dump_state().map(move |state| (id, state)))
            .collect();

        Box::new(join_all(futures).map(|states| ServerStateSnapshot {
            conns: states.into_iter().collect(),
        }))
    }
}

pub struct ServerStateSnapshot {
    pub conns: HashMap<u64, ConnStateSnapshot>,
}

impl ServerStateSnapshot {
    pub fn single_conn(&self) -> (u64, &ConnStateSnapshot) {
        let mut iter = self.conns.iter();
        let (&id, conn) = iter.next().expect("no conns");
        assert!(iter.next().is_none(), "more than one conn");
        (id, conn)
    }
}

fn spawn_server_event_loop<S, A>(
    handle: reactor::Handle,
    state: Arc<Mutex<ServerState>>,
    tls: ServerTlsOption<A>,
    listen: Box<ToTokioListener + Send>,
    exec: CpuPoolOption,
    shutdown_future: ShutdownFuture,
    conf: ServerConf,
    service: S,
    _alive_tx: mpsc::Sender<()>,
) -> oneshot::Receiver<()>
where
    S: ServerHandler,
    A: TlsAcceptor,
{
    let service = Arc::new(service);

    let tokio_listener = listen.to_tokio_listener(&handle);

    let stuff = stream::repeat((handle.clone(), service, state, tls, conf));

    let loop_run = tokio_listener
        .incoming()
        .map_err(Error::from)
        .zip(stuff)
        .for_each(
            move |((socket, peer_addr), (loop_handle, service, state, tls, conf))| {
                if socket.is_tcp() {
                    info!(
                        "accepted connection from {}",
                        peer_addr.downcast_ref::<SocketAddr>().unwrap()
                    );

                    let no_delay = conf.no_delay.unwrap_or(true);
                    socket
                        .set_nodelay(no_delay)
                        .expect("failed to set TCP_NODELAY");
                }

                let (conn, future) =
                    ServerConn::new(&loop_handle, socket, tls, exec.clone(), conf, service);

                let conn_id = {
                    let mut g = state.lock().expect("lock");
                    g.last_conn_id += 1;
                    let conn_id = g.last_conn_id;
                    let prev = g.conns.insert(conn_id, conn);
                    assert!(prev.is_none());
                    conn_id
                };

                loop_handle.spawn(
                    future
                        .then(move |r| {
                            let mut g = state.lock().expect("lock");
                            let removed = g.conns.remove(&conn_id);
                            assert!(removed.is_some());
                            r
                        }).map_err(|e| {
                            warn!("connection end: {:?}", e);
                            ()
                        }),
                );
                Ok(())
            },
        );

    let (done_tx, done_rx) = oneshot::channel();

    let shutdown_future = shutdown_future.then(move |_| {
        // Must complete with error,
        // so `join` with this future cancels another future.
        future::failed::<(), _>(Error::Shutdown)
    });

    // Wait for either completion of connection (i. e. error)
    // or shutdown signal.
    let done = loop_run.join(shutdown_future);

    let done = done.then(|_| {
        drop(done_tx.send(()));
        Ok(())
    });

    handle.spawn(done);

    done_rx
}

impl Server {
    pub fn local_addr(&self) -> &AnySocketAddr {
        &self.local_addr
    }

    pub fn is_alive(&self) -> bool {
        self.alive_rx.try_recv() != Err(mpsc::TryRecvError::Disconnected)
    }

    // for tests
    pub fn dump_state(&self) -> HttpFutureSend<ServerStateSnapshot> {
        let g = self.state.lock().expect("lock");
        g.snapshot()
    }
}

// We shutdown the server in the destructor.
impl Drop for Server {
    fn drop(&mut self) {
        self.shutdown.shutdown();

        // do not ignore errors of take
        // ignore errors of join, it means that server event loop crashed
        match self.join.take().unwrap() {
            Completion::Thread(join) => drop(join.join()),
            Completion::Rx(_rx) => {
                // cannot wait on _rx, because Core might not be running
            }
        };

        self.local_addr.cleanup();
    }
}
<commit_msg>Better message if server listen address is not specified<commit_after>pub mod conf;
pub mod handler;
pub mod handler_paths;
pub mod req;
pub mod resp;
pub mod server_conn;
pub mod server_tls;

use std::collections::HashMap;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;

use tls_api;

use tokio_core::reactor;

use futures::future;
use futures::future::join_all;
use futures::future::Future;
use futures::stream;
use futures::stream::Stream;
use futures::sync::oneshot;

use futures_cpupool;

use exec::CpuPoolOption;

use error::Error;
use result::Result;

use solicit_async::*;

use futures_misc::*;

use tls_api::TlsAcceptor;
use tls_api_stub;

use super::common::*;

use socket::AnySocketAddr;
use socket::ToSocketListener;
use socket::ToTokioListener;

pub use self::server_tls::ServerTlsOption;
pub use server::conf::ServerConf;
use server::handler::ServerHandler;
use server::handler_paths::ServerHandlerPaths;
pub use server::server_conn::ServerConn;

pub struct ServerBuilder<A: tls_api::TlsAcceptor = tls_api_stub::TlsAcceptor> {
    pub conf: ServerConf,
    pub cpu_pool: CpuPoolOption,
    pub tls: ServerTlsOption<A>,
    pub addr: Option<AnySocketAddr>,
    /// Event loop to spawn server.
    /// If not specified, builder will create new event loop in a new thread.
    pub event_loop: Option<reactor::Remote>,
    pub service: ServerHandlerPaths,
}

impl ServerBuilder<tls_api_stub::TlsAcceptor> {
    /// New server builder with defaults.
    ///
    /// Port must be set, other properties are optional.
    pub fn new_plain() -> ServerBuilder<tls_api_stub::TlsAcceptor> {
        ServerBuilder::new()
    }
}

#[cfg(unix)]
impl ServerBuilder<tls_api_stub::TlsAcceptor> {
    /// New unix domain socket server with defaults
    ///
    /// Addr must be set, other properties are optional.
    pub fn new_plain_unix() -> ServerBuilder<tls_api_stub::TlsAcceptor> {
        ServerBuilder::<tls_api_stub::TlsAcceptor>::new()
    }
}

impl<A: tls_api::TlsAcceptor> ServerBuilder<A> {
    /// Set port server listens on.
    /// Can be zero to bind on any available port,
    /// which can be later obtained by `Server::local_addr`.
    pub fn set_port(&mut self, port: u16) {
        self.set_addr(("::", port)).expect("set_addr");
    }

    /// Set port server listens on.
    pub fn set_addr<S: ToSocketAddrs>(&mut self, addr: S) -> Result<()> {
        let addrs: Vec<_> = addr.to_socket_addrs()?.collect();
        if addrs.is_empty() {
            return Err(Error::Other("addr is resolved to empty list"));
        } else if addrs.len() > 1 {
            return Err(Error::Other("addr is resolved to more than one addr"));
        }
        self.addr = Some(AnySocketAddr::Inet(addrs.into_iter().next().unwrap()));
        Ok(())
    }
}

#[cfg(unix)]
impl<A: tls_api::TlsAcceptor> ServerBuilder<A> {
    // Set name of unix domain socket
    pub fn set_unix_addr(&mut self, addr: String) -> Result<()> {
        self.addr = Some(AnySocketAddr::Unix(addr));
        Ok(())
    }
}

impl<A: tls_api::TlsAcceptor> ServerBuilder<A> {
    /// New server builder with defaults.
    ///
    /// To call this function `ServerBuilder` must be parameterized with TLS acceptor.
    /// If TLS is not needed, `ServerBuilder::new_plain` function can be used.
    ///
    /// Port must be set, other properties are optional.
    pub fn new() -> ServerBuilder<A> {
        ServerBuilder {
            conf: ServerConf::new(),
            cpu_pool: CpuPoolOption::SingleThread,
            tls: ServerTlsOption::Plain,
            addr: None,
            event_loop: None,
            service: ServerHandlerPaths::new(),
        }
    }

    /// Create a CPU pool, and use it in HTTP server
    pub fn set_cpu_pool_threads(&mut self, threads: usize) {
        let cpu_pool = futures_cpupool::Builder::new()
            .pool_size(threads)
            .name_prefix("httpbis-server-")
            .create();
        self.cpu_pool = CpuPoolOption::CpuPool(cpu_pool);
    }

    pub fn set_tls(&mut self, acceptor: A) {
        self.tls = ServerTlsOption::Tls(Arc::new(acceptor));
    }

    pub fn build(self) -> Result<Server> {
        let (alive_tx, alive_rx) = mpsc::channel();

        let state: Arc<Mutex<ServerState>> = Default::default();

        let state_copy = state.clone();

        let (shutdown_signal, shutdown_future) = shutdown_signal();

        // TODO: why done_tx is unused?
        let (_done_tx, done_rx) = oneshot::channel();

        // TODO: return result
        let listen = self
            .addr
            .expect("listen addr not specified")
            .to_listener(&self.conf);

        let local_addr = listen.local_addr().unwrap();
        //let local_addr = local_addr.downcast_ref::<T>().expect("downcast socket_addr").clone();

        let join = if let Some(remote) = self.event_loop {
            let tls = self.tls;
            let cpu_pool = self.cpu_pool;
            let conf = self.conf;
            let service = self.service;
            remote.spawn(move |handle| {
                drop(spawn_server_event_loop(
                    handle.clone(),
                    state_copy,
                    tls,
                    listen,
                    cpu_pool,
                    shutdown_future,
                    conf,
                    service,
                    alive_tx,
                ));
                future::finished(())
            });
            Completion::Rx(done_rx)
        } else {
            let tls = self.tls;
            let cpu_pool = self.cpu_pool;
            let conf = self.conf;
            let service = self.service;
            let join_handle = thread::Builder::new()
                .name(
                    conf.thread_name
                        .clone()
                        .unwrap_or_else(|| "http2-server-loop".to_owned())
                        .to_string(),
                ).spawn(move || {
                    let mut lp = reactor::Core::new().expect("http2server");
                    let done_rx = spawn_server_event_loop(
                        lp.handle(),
                        state_copy,
                        tls,
                        listen,
                        cpu_pool,
                        shutdown_future,
                        conf,
                        service,
                        alive_tx,
                    );
                    drop(lp.run(done_rx));
                })?;
            Completion::Thread(join_handle)
        };

        Ok(Server {
            state: state,
            shutdown: shutdown_signal,
            local_addr: local_addr,
            join: Some(join),
            alive_rx: alive_rx,
        })
    }
}

enum Completion {
    Thread(thread::JoinHandle<()>),
    Rx(oneshot::Receiver<()>),
}

pub struct Server {
    state: Arc<Mutex<ServerState>>,
    local_addr: AnySocketAddr,
    shutdown: ShutdownSignal,
    alive_rx: mpsc::Receiver<()>,
    join: Option<Completion>,
}

#[derive(Default)]
struct ServerState {
    last_conn_id: u64,
    conns: HashMap<u64, ServerConn>,
}

impl ServerState {
    fn snapshot(&self) -> HttpFutureSend<ServerStateSnapshot> {
        let futures: Vec<_> = self
            .conns
            .iter()
            .map(|(&id, conn)| conn.dump_state().map(move |state| (id, state)))
            .collect();

        Box::new(join_all(futures).map(|states| ServerStateSnapshot {
            conns: states.into_iter().collect(),
        }))
    }
}

pub struct ServerStateSnapshot {
    pub conns: HashMap<u64, ConnStateSnapshot>,
}

impl ServerStateSnapshot {
    pub fn single_conn(&self) -> (u64, &ConnStateSnapshot) {
        let mut iter = self.conns.iter();
        let (&id, conn) = iter.next().expect("no conns");
        assert!(iter.next().is_none(), "more than one conn");
        (id, conn)
    }
}

fn spawn_server_event_loop<S, A>(
    handle: reactor::Handle,
    state: Arc<Mutex<ServerState>>,
    tls: ServerTlsOption<A>,
    listen: Box<ToTokioListener + Send>,
    exec: CpuPoolOption,
    shutdown_future: ShutdownFuture,
    conf: ServerConf,
    service: S,
    _alive_tx: mpsc::Sender<()>,
) -> oneshot::Receiver<()>
where
    S: ServerHandler,
    A: TlsAcceptor,
{
    let service = Arc::new(service);

    let tokio_listener = listen.to_tokio_listener(&handle);

    let stuff = stream::repeat((handle.clone(), service, state, tls, conf));

    let loop_run = tokio_listener
        .incoming()
        .map_err(Error::from)
        .zip(stuff)
        .for_each(
            move |((socket, peer_addr), (loop_handle, service, state, tls, conf))| {
                if socket.is_tcp() {
                    info!(
                        "accepted connection from {}",
                        peer_addr.downcast_ref::<SocketAddr>().unwrap()
                    );

                    let no_delay = conf.no_delay.unwrap_or(true);
                    socket
                        .set_nodelay(no_delay)
                        .expect("failed to set TCP_NODELAY");
                }

                let (conn, future) =
                    ServerConn::new(&loop_handle, socket, tls, exec.clone(), conf, service);

                let conn_id = {
                    let mut g = state.lock().expect("lock");
                    g.last_conn_id += 1;
                    let conn_id = g.last_conn_id;
                    let prev = g.conns.insert(conn_id, conn);
                    assert!(prev.is_none());
                    conn_id
                };

                loop_handle.spawn(
                    future
                        .then(move |r| {
                            let mut g = state.lock().expect("lock");
                            let removed = g.conns.remove(&conn_id);
                            assert!(removed.is_some());
                            r
                        }).map_err(|e| {
                            warn!("connection end: {:?}", e);
                            ()
                        }),
                );
                Ok(())
            },
        );

    let (done_tx, done_rx) = oneshot::channel();

    let shutdown_future = shutdown_future.then(move |_| {
        // Must complete with error,
        // so `join` with this future cancels another future.
        future::failed::<(), _>(Error::Shutdown)
    });

    // Wait for either completion of connection (i. e. error)
    // or shutdown signal.
    let done = loop_run.join(shutdown_future);

    let done = done.then(|_| {
        drop(done_tx.send(()));
        Ok(())
    });

    handle.spawn(done);

    done_rx
}

impl Server {
    pub fn local_addr(&self) -> &AnySocketAddr {
        &self.local_addr
    }

    pub fn is_alive(&self) -> bool {
        self.alive_rx.try_recv() != Err(mpsc::TryRecvError::Disconnected)
    }

    // for tests
    pub fn dump_state(&self) -> HttpFutureSend<ServerStateSnapshot> {
        let g = self.state.lock().expect("lock");
        g.snapshot()
    }
}

// We shutdown the server in the destructor.
impl Drop for Server {
    fn drop(&mut self) {
        self.shutdown.shutdown();

        // do not ignore errors of take
        // ignore errors of join, it means that server event loop crashed
        match self.join.take().unwrap() {
            Completion::Thread(join) => drop(join.join()),
            Completion::Rx(_rx) => {
                // cannot wait on _rx, because Core might not be running
            }
        };

        self.local_addr.cleanup();
    }
}
<|endoftext|>


<commit_before>mod endpoint;
mod model;

use crate::database::settings::{PRIVACY_ADMIN_ONLY, PRIVACY_EVERYONE, PRIVACY_STAFF_ONLY};
use crate::discord::util::is_staff;
use crate::server::endpoint::ban::{get_ban, get_bans, update_ban};
use crate::server::endpoint::captcha::{get_captcha_page, submit_captcha};
use crate::server::endpoint::hardban::{get_hardban, get_hardbans, update_hardban};
use crate::server::endpoint::kick::{get_kick, get_kicks, update_kick};
use crate::server::endpoint::login::login;
use crate::server::endpoint::mute::{get_mute, get_mutes, update_mute};
use crate::server::endpoint::self_user::get_self;
use crate::server::endpoint::settings::{get_setting, reset_setting, update_setting};
use crate::server::endpoint::softban::{get_softban, get_softbans, update_softban};
use crate::server::endpoint::warn::{get_warn, get_warns, update_warn};
use crate::service::guild::{CachedMember, GuildService};
use crate::service::invalid_uuid::InvalidUUIDService;
use crate::service::setting::SettingService;
use crate::util::now;
use crate::Config;
use actix_cors::Cors;
use actix_web::http::header::{HeaderName, CONTENT_TYPE};
use actix_web::{get, web, App, HttpRequest, HttpResponse, HttpServer, Responder};
use jsonwebtoken::errors::ErrorKind;
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use serenity::model::id::{GuildId, UserId};
use serenity::model::Permissions;
use std::error::Error;
use std::net::Ipv4Addr;
use std::sync::Arc;
use tracing::error;
use typemap_rev::TypeMap;
use uuid::Uuid;

lazy_static! {
    static ref VALIDATION: Validation = Validation {
        algorithms: vec![Algorithm::HS512],
        ..Validation::default()
    };
    static ref ENCODING_HEADER: Header = Header::new(Algorithm::HS512);
}

#[derive(Deserialize, Serialize)]
struct JwtClaims {
    #[serde(rename = "userId")]
    user_id: String,
    uuid: Uuid,
    exp: u64,
}

fn verify_token(secret: &str, token: &str) -> Option<JwtClaims> {
    match decode::<JwtClaims>(
        token,
        &DecodingKey::from_secret(secret.as_bytes()),
        &VALIDATION,
    ) {
        Ok(data) => Some(data.claims),
        Err(error) => match error.kind() {
            ErrorKind::InvalidSignature | ErrorKind::ExpiredSignature => None,
            _ => {
                error!("failed to decode jwt token {:?}", error.kind());
                None
            }
        },
    }
}

fn generate_token(secret: &str, user_id: UserId) -> Option<String> {
    let user_id = user_id.to_string();
    let expires_at = now() + 7 * 24 * 60 * 60;
    let uuid = Uuid::new_v4();
    let claims = JwtClaims {
        user_id,
        uuid,
        exp: expires_at,
    };

    match encode(
        &ENCODING_HEADER,
        &claims,
        &EncodingKey::from_secret(secret.as_bytes()),
    ) {
        Ok(token) => Some(token),
        Err(err) => {
            error!("failed to create jwt token {:?}", err);
            None
        }
    }
}

async fn is_authenticated(
    config: &Config,
    services: &TypeMap,
    req: &web::HttpRequest,
) -> Option<UserId> {
    let token = req
        .headers()
        .get(HeaderName::from_static("token")) // optimize into constant?
        .map(|value| value.to_str())
        .map(|result| result.ok())
        .flatten()?;

    let claims = verify_token(&config.server_secret, token)?;

    let invalid_uuid_service = services.get::<InvalidUUIDService>()?;

    if invalid_uuid_service.is_uuid_invalid(claims.uuid).await {
        None
    } else {
        match claims.user_id.parse::<u64>() {
            Ok(id) => Some(UserId(id)),
            Err(_) => {
                // secret leaked or we have a problem with token generation
                error!("received a token with valid signature with invalid user id");
                None
            }
        }
    }
}

pub async fn check_authentication(
    config: &Config,
    services: &TypeMap,
    req: &web::HttpRequest,
) -> Result<UserId, HttpResponse> {
    is_authenticated(config, services, req)
        .await
        .ok_or_else(|| HttpResponse::Unauthorized().json("Invalid token!"))
}

pub enum PrivateEndpointKind {
    ModLog,
    Settings,
}

async fn is_authorized_to_view_private_endpoint(
    services: &TypeMap,
    guild_id: GuildId,
    permissions: Permissions,
    endpoint_kind: PrivateEndpointKind,
) -> bool {
    let setting_service = if let Some(service) = services.get::<SettingService>() {
        service
    } else {
        return false;
    };

    let setting = setting_service.get_setting(guild_id).await;
    let privacy_setting = match endpoint_kind {
        PrivateEndpointKind::Settings => setting.privacy_settings,
        PrivateEndpointKind::ModLog => setting.privacy_mod_log,
    };

    if privacy_setting == PRIVACY_EVERYONE {
        true
    } else if privacy_setting == PRIVACY_STAFF_ONLY {
        is_staff(permissions)
    } else if privacy_setting == PRIVACY_ADMIN_ONLY {
        permissions.administrator()
    } else {
        false
    }
}

pub async fn check_authorization_to_view_private_endpoint(
    services: &TypeMap,
    guild_id: GuildId,
    permissions: Permissions,
    endpoint_kind: PrivateEndpointKind,
) -> Option<HttpResponse> {
    if is_authorized_to_view_private_endpoint(services, guild_id, permissions, endpoint_kind).await
    {
        None
    } else {
        Some(
            HttpResponse::Forbidden()
                .json("Server settings prevent you from viewing private information!"),
        )
    }
}

pub async fn is_guild_endpoint(
    services: &TypeMap,
    guild_id: GuildId,
    user_id: UserId,
) -> Option<(Arc<CachedMember>, Permissions)> {
    let guild_service = services.get::<GuildService>()?;

    let member = guild_service.get_member(guild_id, user_id).await.ok()?;

    let permissions = if let Ok(permissions) = guild_service
        .get_permissions(user_id, &member.roles, guild_id)
        .await
    {
        permissions
    } else {
        return None;
    };

    Some((member, permissions))
}

pub async fn check_guild_endpoint(
    services: &TypeMap,
    guild_id: GuildId,
    user_id: UserId,
) -> Result<(Arc<CachedMember>, Permissions), HttpResponse> {
    is_guild_endpoint(services, guild_id, user_id)
        .await
        .ok_or_else(|| {
            HttpResponse::Forbidden().json("This server either doesn't exist or you aren't in it!")
        })
}

pub async fn apply_private_endpoint_fetch_checks(
    config: &Config,
    services: &TypeMap,
    req: &HttpRequest,
    guild_id: GuildId,
    endpoint_kind: PrivateEndpointKind,
) -> Result<(), HttpResponse> {
    let user_id = match check_authentication(config, services, req).await {
        Ok(user_id) => user_id,
        Err(response) => return Err(response),
    };

    let (_, permissions) = match check_guild_endpoint(services, guild_id, user_id).await {
        Ok((member, permissions)) => (member, permissions),
        Err(response) => return Err(response),
    };

    if let Some(response) =
        check_authorization_to_view_private_endpoint(services, guild_id, permissions, endpoint_kind)
            .await
    {
        return Err(response);
    }

    Ok(())
}

pub async fn apply_mod_log_update_checks(
    config: &Config,
    services: &TypeMap,
    req: &HttpRequest,
    guild_id: GuildId,
    required_permission: Permissions,
) -> Result<(), HttpResponse> {
    let user_id = match check_authentication(config, services, req).await {
        Ok(user_id) => user_id,
        Err(response) => return Err(response),
    };

    let (_, permissions) = match check_guild_endpoint(services, guild_id, user_id).await {
        Ok((member, permissions)) => (member, permissions),
        Err(response) => return Err(response),
    };

    if permissions.contains(Permissions::ADMINISTRATOR) || permissions.contains(required_permission)
    {
        Ok(())
    } else {
        Err(HttpResponse::Forbidden()
            .json("You don't have permissions to change moderator log history!"))
    }
}

pub async fn apply_setting_update_checks(
    config: &Config,
    services: &TypeMap,
    req: &HttpRequest,
    guild_id: GuildId,
) -> Result<(), HttpResponse> {
    let user_id = match check_authentication(config, services, req).await {
        Ok(user_id) => user_id,
        Err(response) => return Err(response),
    };

    let (_, permissions) = match check_guild_endpoint(services, guild_id, user_id).await {
        Ok((member, permissions)) => (member, permissions),
        Err(response) => return Err(response),
    };

    if permissions.contains(Permissions::ADMINISTRATOR) {
        Ok(())
    } else {
        Err(HttpResponse::Forbidden().json("You don't have permissions to change server settings!"))
    }
}

#[get("/")]
async fn index() -> impl Responder {
    "Welcome to Safety Jim API."
}

pub async fn run_server(config: Arc<Config>, services: Arc<TypeMap>) -> Result<(), Box<dyn Error>> {
    let port = config.server_port;
    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::from(config.clone()))
            .app_data(web::Data::from(services.clone()))
            .service(index)
            .service(get_self)
            .service(get_bans)
            .service(get_ban)
            .service(update_ban)
            .service(get_captcha_page)
            .service(submit_captcha)
            .service(get_hardbans)
            .service(get_hardban)
            .service(update_hardban)
            .service(get_kicks)
            .service(get_kick)
            .service(update_kick)
            .service(login)
            .service(get_mutes)
            .service(get_mute)
            .service(update_mute)
            .service(get_setting)
            .service(update_setting)
            .service(reset_setting)
            .service(get_softbans)
            .service(get_softban)
            .service(update_softban)
            .service(get_warns)
            .service(get_warn)
            .service(update_warn)
            .wrap(
                Cors::default()
                    .allowed_origin(&config.cors_origin)
                    // needed for captcha endpoint, actix-cors doesn't actually check if request is for CORS
                    .allowed_origin(&config.self_url)
                    .allowed_methods(vec!["GET", "POST", "DELETE"])
                    .allowed_headers(vec![CONTENT_TYPE, HeaderName::from_static("token")])
                    .max_age(None),
            )
    })
    .bind((Ipv4Addr::LOCALHOST, port))?
    .run()
    .await?;

    Ok(())
}
<commit_msg>Adapt to actix-web changes for jwt<commit_after>mod endpoint;
mod model;

use crate::database::settings::{PRIVACY_ADMIN_ONLY, PRIVACY_EVERYONE, PRIVACY_STAFF_ONLY};
use crate::discord::util::is_staff;
use crate::server::endpoint::ban::{get_ban, get_bans, update_ban};
use crate::server::endpoint::captcha::{get_captcha_page, submit_captcha};
use crate::server::endpoint::hardban::{get_hardban, get_hardbans, update_hardban};
use crate::server::endpoint::kick::{get_kick, get_kicks, update_kick};
use crate::server::endpoint::login::login;
use crate::server::endpoint::mute::{get_mute, get_mutes, update_mute};
use crate::server::endpoint::self_user::get_self;
use crate::server::endpoint::settings::{get_setting, reset_setting, update_setting};
use crate::server::endpoint::softban::{get_softban, get_softbans, update_softban};
use crate::server::endpoint::warn::{get_warn, get_warns, update_warn};
use crate::service::guild::{CachedMember, GuildService};
use crate::service::invalid_uuid::InvalidUUIDService;
use crate::service::setting::SettingService;
use crate::util::now;
use crate::Config;
use actix_cors::Cors;
use actix_web::http::header::{HeaderName, CONTENT_TYPE};
use actix_web::{get, web, App, HttpRequest, HttpResponse, HttpServer, Responder};
use jsonwebtoken::errors::ErrorKind;
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use serenity::model::id::{GuildId, UserId};
use serenity::model::Permissions;
use std::error::Error;
use std::net::Ipv4Addr;
use std::sync::Arc;
use tracing::error;
use typemap_rev::TypeMap;
use uuid::Uuid;

lazy_static! {
    static ref VALIDATION: Validation = Validation::new(Algorithm::HS512);
    static ref ENCODING_HEADER: Header = Header::new(Algorithm::HS512);
}

#[derive(Deserialize, Serialize)]
struct JwtClaims {
    #[serde(rename = "userId")]
    user_id: String,
    uuid: Uuid,
    exp: u64,
}

fn verify_token(secret: &str, token: &str) -> Option<JwtClaims> {
    match decode::<JwtClaims>(
        token,
        &DecodingKey::from_secret(secret.as_bytes()),
        &VALIDATION,
    ) {
        Ok(data) => Some(data.claims),
        Err(error) => match error.kind() {
            ErrorKind::InvalidSignature | ErrorKind::ExpiredSignature => None,
            _ => {
                error!("failed to decode jwt token {:?}", error.kind());
                None
            }
        },
    }
}

fn generate_token(secret: &str, user_id: UserId) -> Option<String> {
    let user_id = user_id.to_string();
    let expires_at = now() + 7 * 24 * 60 * 60;
    let uuid = Uuid::new_v4();
    let claims = JwtClaims {
        user_id,
        uuid,
        exp: expires_at,
    };

    match encode(
        &ENCODING_HEADER,
        &claims,
        &EncodingKey::from_secret(secret.as_bytes()),
    ) {
        Ok(token) => Some(token),
        Err(err) => {
            error!("failed to create jwt token {:?}", err);
            None
        }
    }
}

async fn is_authenticated(
    config: &Config,
    services: &TypeMap,
    req: &web::HttpRequest,
) -> Option<UserId> {
    let token = req
        .headers()
        .get(HeaderName::from_static("token")) // optimize into constant?
        .map(|value| value.to_str())
        .map(|result| result.ok())
        .flatten()?;

    let claims = verify_token(&config.server_secret, token)?;

    let invalid_uuid_service = services.get::<InvalidUUIDService>()?;

    if invalid_uuid_service.is_uuid_invalid(claims.uuid).await {
        None
    } else {
        match claims.user_id.parse::<u64>() {
            Ok(id) => Some(UserId(id)),
            Err(_) => {
                // secret leaked or we have a problem with token generation
                error!("received a token with valid signature with invalid user id");
                None
            }
        }
    }
}

pub async fn check_authentication(
    config: &Config,
    services: &TypeMap,
    req: &web::HttpRequest,
) -> Result<UserId, HttpResponse> {
    is_authenticated(config, services, req)
        .await
        .ok_or_else(|| HttpResponse::Unauthorized().json("Invalid token!"))
}

pub enum PrivateEndpointKind {
    ModLog,
    Settings,
}

async fn is_authorized_to_view_private_endpoint(
    services: &TypeMap,
    guild_id: GuildId,
    permissions: Permissions,
    endpoint_kind: PrivateEndpointKind,
) -> bool {
    let setting_service = if let Some(service) = services.get::<SettingService>() {
        service
    } else {
        return false;
    };

    let setting = setting_service.get_setting(guild_id).await;
    let privacy_setting = match endpoint_kind {
        PrivateEndpointKind::Settings => setting.privacy_settings,
        PrivateEndpointKind::ModLog => setting.privacy_mod_log,
    };

    if privacy_setting == PRIVACY_EVERYONE {
        true
    } else if privacy_setting == PRIVACY_STAFF_ONLY {
        is_staff(permissions)
    } else if privacy_setting == PRIVACY_ADMIN_ONLY {
        permissions.administrator()
    } else {
        false
    }
}

pub async fn check_authorization_to_view_private_endpoint(
    services: &TypeMap,
    guild_id: GuildId,
    permissions: Permissions,
    endpoint_kind: PrivateEndpointKind,
) -> Option<HttpResponse> {
    if is_authorized_to_view_private_endpoint(services, guild_id, permissions, endpoint_kind).await
    {
        None
    } else {
        Some(
            HttpResponse::Forbidden()
                .json("Server settings prevent you from viewing private information!"),
        )
    }
}

pub async fn is_guild_endpoint(
    services: &TypeMap,
    guild_id: GuildId,
    user_id: UserId,
) -> Option<(Arc<CachedMember>, Permissions)> {
    let guild_service = services.get::<GuildService>()?;

    let member = guild_service.get_member(guild_id, user_id).await.ok()?;

    let permissions = if let Ok(permissions) = guild_service
        .get_permissions(user_id, &member.roles, guild_id)
        .await
    {
        permissions
    } else {
        return None;
    };

    Some((member, permissions))
}

pub async fn check_guild_endpoint(
    services: &TypeMap,
    guild_id: GuildId,
    user_id: UserId,
) -> Result<(Arc<CachedMember>, Permissions), HttpResponse> {
    is_guild_endpoint(services, guild_id, user_id)
        .await
        .ok_or_else(|| {
            HttpResponse::Forbidden().json("This server either doesn't exist or you aren't in it!")
        })
}

pub async fn apply_private_endpoint_fetch_checks(
    config: &Config,
    services: &TypeMap,
    req: &HttpRequest,
    guild_id: GuildId,
    endpoint_kind: PrivateEndpointKind,
) -> Result<(), HttpResponse> {
    let user_id = match check_authentication(config, services, req).await {
        Ok(user_id) => user_id,
        Err(response) => return Err(response),
    };

    let (_, permissions) = match check_guild_endpoint(services, guild_id, user_id).await {
        Ok((member, permissions)) => (member, permissions),
        Err(response) => return Err(response),
    };

    if let Some(response) =
        check_authorization_to_view_private_endpoint(services, guild_id, permissions, endpoint_kind)
            .await
    {
        return Err(response);
    }

    Ok(())
}

pub async fn apply_mod_log_update_checks(
    config: &Config,
    services: &TypeMap,
    req: &HttpRequest,
    guild_id: GuildId,
    required_permission: Permissions,
) -> Result<(), HttpResponse> {
    let user_id = match check_authentication(config, services, req).await {
        Ok(user_id) => user_id,
        Err(response) => return Err(response),
    };

    let (_, permissions) = match check_guild_endpoint(services, guild_id, user_id).await {
        Ok((member, permissions)) => (member, permissions),
        Err(response) => return Err(response),
    };

    if permissions.contains(Permissions::ADMINISTRATOR) || permissions.contains(required_permission)
    {
        Ok(())
    } else {
        Err(HttpResponse::Forbidden()
            .json("You don't have permissions to change moderator log history!"))
    }
}

pub async fn apply_setting_update_checks(
    config: &Config,
    services: &TypeMap,
    req: &HttpRequest,
    guild_id: GuildId,
) -> Result<(), HttpResponse> {
    let user_id = match check_authentication(config, services, req).await {
        Ok(user_id) => user_id,
        Err(response) => return Err(response),
    };

    let (_, permissions) = match check_guild_endpoint(services, guild_id, user_id).await {
        Ok((member, permissions)) => (member, permissions),
        Err(response) => return Err(response),
    };

    if permissions.contains(Permissions::ADMINISTRATOR) {
        Ok(())
    } else {
        Err(HttpResponse::Forbidden().json("You don't have permissions to change server settings!"))
    }
}

#[get("/")]
async fn index() -> impl Responder {
    "Welcome to Safety Jim API."
}

pub async fn run_server(config: Arc<Config>, services: Arc<TypeMap>) -> Result<(), Box<dyn Error>> {
    let port = config.server_port;
    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::from(config.clone()))
            .app_data(web::Data::from(services.clone()))
            .service(index)
            .service(get_self)
            .service(get_bans)
            .service(get_ban)
            .service(update_ban)
            .service(get_captcha_page)
            .service(submit_captcha)
            .service(get_hardbans)
            .service(get_hardban)
            .service(update_hardban)
            .service(get_kicks)
            .service(get_kick)
            .service(update_kick)
            .service(login)
            .service(get_mutes)
            .service(get_mute)
            .service(update_mute)
            .service(get_setting)
            .service(update_setting)
            .service(reset_setting)
            .service(get_softbans)
            .service(get_softban)
            .service(update_softban)
            .service(get_warns)
            .service(get_warn)
            .service(update_warn)
            .wrap(
                Cors::default()
                    .allowed_origin(&config.cors_origin)
                    // needed for captcha endpoint, actix-cors doesn't actually check if request is for CORS
                    .allowed_origin(&config.self_url)
                    .allowed_methods(vec!["GET", "POST", "DELETE"])
                    .allowed_headers(vec![CONTENT_TYPE, HeaderName::from_static("token")])
                    .max_age(None),
            )
    })
    .bind((Ipv4Addr::LOCALHOST, port))?
    .run()
    .await?;

    Ok(())
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use metric::Metric;
use std::rc::Rc;
use registry::Registry;
use counter::Counter;
use ewma::EWMA;
use gauge::Gauge;
use meter::Meter;

pub trait Reporter {
    fn report(self, report_card: ReportCard);

    fn get_unique_reporter_name(&self) -> &'static str;
}

pub struct ReportCard {
    metrics: HashMap<String, Box<Metric>>,
}

impl Reporter for ConsoleReporter {
    fn report(self, report_card: ReportCard ) {

    }

    fn get_unique_reporter_name(&self) -> &'static str {
        "console-reporter"
    }
}

pub struct ConsoleReporter;
/*

impl<'a> ConsoleReporter<'a> {
    pub fn new(registry_to_report_to_console:Rc<Registry<'a>>) -> ConsoleReporter<'a> {
        ConsoleReporter {registry: registry_to_report_to_console}
    }
}

#[cfg(test)]
mod test {
    use meter::StdMeter;
    use registry::{Registry, StdRegistry};

    #[test]
    fn meter() {
        let mut r: StdRegistry = StdRegistry::new();
        let m: StdMeter = StdMeter::new();

        r.insert("foo", m);
        r.get("foo");
    }
}
*/
<commit_msg>Allow the reporter name to be configurable<commit_after>use std::collections::HashMap;

use metric::Metric;
use std::rc::Rc;
use registry::Registry;
use counter::Counter;
use ewma::EWMA;
use gauge::Gauge;
use meter::Meter;

pub trait Reporter {
    fn report(self, report_card: ReportCard);

    fn get_unique_reporter_name(&self) -> &'static str;
}

pub struct ReportCard {
    metrics: HashMap<String, Box<Metric>>,
}

impl Reporter for ConsoleReporter {
    fn report(self, report_card: ReportCard ) {

    }

    fn get_unique_reporter_name(&self) -> &'static str {
        self.reporter_name
    }
}

pub struct ConsoleReporter {
    reporter_name : & 'static str
}

impl ConsoleReporter {
    pub fn new(reporter_name: & 'static str) -> ConsoleReporter {
        ConsoleReporter {reporter_name : reporter_name}
    }
}

/*
#[cfg(test)]
mod test {
    use meter::StdMeter;
    use registry::{Registry, StdRegistry};

    #[test]
    fn meter() {
        let mut r: StdRegistry = StdRegistry::new();
        let m: StdMeter = StdMeter::new();

        r.insert("foo", m);
        r.get("foo");
    }
}
*/
<|endoftext|>


<commit_before>use std::sync::{
    Arc,
    Mutex,
};

use c_ares;
use futures;
use futures::Future;

use error::Error;
use eventloop::{
    EventLoop,
    EventLoopHandle
};

/// Used to configure the behaviour of the resolver.
#[derive(Default)]
pub struct Options {
    inner: c_ares::Options,
}

impl Options {
    /// Returns a fresh `Options`, on which no values are set.
    pub fn new() -> Self {
        Self::default()
    }

    /// Set flags controlling the behaviour of the resolver.  The available
    /// flags are documented [here](flags/index.html).
    pub fn set_flags(&mut self, flags: c_ares::flags::Flags) -> &mut Self {
        self.inner.set_flags(flags);
        self
    }

    /// Set the number of milliseconds each name server is given to respond to
    /// a query on the first try.  (After the first try, the timeout algorithm
    /// becomes more complicated, but scales linearly with the value of
    /// timeout).  The default is 5000ms.
    pub fn set_timeout(&mut self, ms: u32) -> &mut Self {
        self.inner.set_timeout(ms);
        self
    }

    /// Set the number of tries the resolver will try contacting each name
    /// server before giving up.  The default is four tries.
    pub fn set_tries(&mut self, tries: u32) -> &mut Self {
        self.inner.set_tries(tries);
        self
    }

    /// Set the number of dots which must be present in a domain name for it to
    /// be queried for "as is" prior to querying for it with the default domain
    /// extensions appended.  The default value is 1 unless set otherwise by
    /// resolv.conf or the RES_OPTIONS environment variable.
    pub fn set_ndots(&mut self, ndots: u32) -> &mut Self {
        self.inner.set_ndots(ndots);
        self
    }

    /// Set the UDP port to use for queries.  The default value is 53, the
    /// standard name service port.
    pub fn set_udp_port(&mut self, udp_port: u16) -> &mut Self {
        self.inner.set_udp_port(udp_port);
        self
    }

    /// Set the TCP port to use for queries.  The default value is 53, the
    /// standard name service port.
    pub fn set_tcp_port(&mut self, tcp_port: u16) -> &mut Self {
        self.inner.set_tcp_port(tcp_port);
        self
    }

    /// Set the domains to search, instead of the domains specified in
    /// resolv.conf or the domain derived from the kernel hostname variable.
    pub fn set_domains(&mut self, domains: &[&str]) -> &mut Self {
        self.inner.set_domains(domains);
        self
    }

    /// Set the lookups to perform for host queries. `lookups` should be set to
    /// a string of the characters "b" or "f", where "b" indicates a DNS lookup
    /// and "f" indicates a lookup in the hosts file.
    pub fn set_lookups(&mut self, lookups: &str) -> &mut Self {
        self.inner.set_lookups(lookups);
        self
    }

    /// Set the socket send buffer size.
    pub fn set_sock_send_buffer_size(&mut self, size: u32) -> &mut Self {
        self.inner.set_sock_send_buffer_size(size);
        self
    }

    /// Set the socket receive buffer size.
    pub fn set_sock_receive_buffer_size(&mut self, size: u32) -> &mut Self {
        self.inner.set_sock_receive_buffer_size(size);
        self
    }

    /// Configure round robin selection of nameservers.
    pub fn set_rotate(&mut self) -> &mut Self {
        self.inner.set_rotate();
        self
    }

    /// Prevent round robin selection of nameservers.
    pub fn set_no_rotate(&mut self) -> &mut Self {
        self.inner.set_no_rotate();
        self
    }

    /// Set the EDNS packet size.
    pub fn set_ednspsz(&mut self, size: u32) -> &mut Self {
        self.inner.set_ednspsz(size);
        self
    }
}

/// An asynchronous DNS resolver.
pub struct Resolver {
    ares_channel: Arc<Mutex<c_ares::Channel>>,
    event_loop_handle: EventLoopHandle,
}

impl Resolver {
    /// Create a new Resolver.
    pub fn new(options: Options) -> Result<Resolver, Error> {
        // Create and run the event loop.
        let event_loop = try!(EventLoop::new(options.inner));
        let channel = event_loop.ares_channel.clone();
        let handle = event_loop.run();

        // Return the Resolver.
        let resolver = Resolver {
            ares_channel: channel,
            event_loop_handle: handle,
        };
        Ok(resolver)
    }

    pub fn query_cname(&self, name: &str)
        -> futures::BoxFuture<c_ares::CNameResults, c_ares::Error> {
        let (c, p) = futures::oneshot();
        self.ares_channel.lock().unwrap().query_cname(name, move |result| {
            c.complete(result);
        });
        p.map_err(|_| c_ares::Error::ECANCELLED)
            .and_then(futures::done)
            .boxed()
    }

    pub fn query_mx(&self, name: &str)
        -> futures::BoxFuture<c_ares::MXResults, c_ares::Error> {
        let (c, p) = futures::oneshot();
        self.ares_channel.lock().unwrap().query_mx(name, move |result| {
            c.complete(result);
        });
        p.map_err(|_| c_ares::Error::ECANCELLED)
            .and_then(futures::done)
            .boxed()
    }

    pub fn query_naptr(&self, name: &str)
        -> futures::BoxFuture<c_ares::NAPTRResults, c_ares::Error> {
        let (c, p) = futures::oneshot();
        self.ares_channel.lock().unwrap().query_naptr(name, move |result| {
            c.complete(result);
        });
        p.map_err(|_| c_ares::Error::ECANCELLED)
            .and_then(futures::done)
            .boxed()
    }
}

impl Drop for Resolver {
    fn drop(&mut self) {
        // Shut down the event loop.
        self.event_loop_handle.shutdown();
    }
}
<commit_msg>Flesh out resolver interface<commit_after>use std::net::{
    Ipv4Addr,
    Ipv6Addr,
};
use std::sync::{
    Arc,
    Mutex,
};

use c_ares;
use futures;
use futures::Future;

use error::Error;
use eventloop::{
    EventLoop,
    EventLoopHandle
};

/// Used to configure the behaviour of the resolver.
#[derive(Default)]
pub struct Options {
    inner: c_ares::Options,
}

impl Options {
    /// Returns a fresh `Options`, on which no values are set.
    pub fn new() -> Self {
        Self::default()
    }

    /// Set flags controlling the behaviour of the resolver.  The available
    /// flags are documented [here](flags/index.html).
    pub fn set_flags(&mut self, flags: c_ares::flags::Flags) -> &mut Self {
        self.inner.set_flags(flags);
        self
    }

    /// Set the number of milliseconds each name server is given to respond to
    /// a query on the first try.  (After the first try, the timeout algorithm
    /// becomes more complicated, but scales linearly with the value of
    /// timeout).  The default is 5000ms.
    pub fn set_timeout(&mut self, ms: u32) -> &mut Self {
        self.inner.set_timeout(ms);
        self
    }

    /// Set the number of tries the resolver will try contacting each name
    /// server before giving up.  The default is four tries.
    pub fn set_tries(&mut self, tries: u32) -> &mut Self {
        self.inner.set_tries(tries);
        self
    }

    /// Set the number of dots which must be present in a domain name for it to
    /// be queried for "as is" prior to querying for it with the default domain
    /// extensions appended.  The default value is 1 unless set otherwise by
    /// resolv.conf or the RES_OPTIONS environment variable.
    pub fn set_ndots(&mut self, ndots: u32) -> &mut Self {
        self.inner.set_ndots(ndots);
        self
    }

    /// Set the UDP port to use for queries.  The default value is 53, the
    /// standard name service port.
    pub fn set_udp_port(&mut self, udp_port: u16) -> &mut Self {
        self.inner.set_udp_port(udp_port);
        self
    }

    /// Set the TCP port to use for queries.  The default value is 53, the
    /// standard name service port.
    pub fn set_tcp_port(&mut self, tcp_port: u16) -> &mut Self {
        self.inner.set_tcp_port(tcp_port);
        self
    }

    /// Set the domains to search, instead of the domains specified in
    /// resolv.conf or the domain derived from the kernel hostname variable.
    pub fn set_domains(&mut self, domains: &[&str]) -> &mut Self {
        self.inner.set_domains(domains);
        self
    }

    /// Set the lookups to perform for host queries. `lookups` should be set to
    /// a string of the characters "b" or "f", where "b" indicates a DNS lookup
    /// and "f" indicates a lookup in the hosts file.
    pub fn set_lookups(&mut self, lookups: &str) -> &mut Self {
        self.inner.set_lookups(lookups);
        self
    }

    /// Set the socket send buffer size.
    pub fn set_sock_send_buffer_size(&mut self, size: u32) -> &mut Self {
        self.inner.set_sock_send_buffer_size(size);
        self
    }

    /// Set the socket receive buffer size.
    pub fn set_sock_receive_buffer_size(&mut self, size: u32) -> &mut Self {
        self.inner.set_sock_receive_buffer_size(size);
        self
    }

    /// Configure round robin selection of nameservers.
    pub fn set_rotate(&mut self) -> &mut Self {
        self.inner.set_rotate();
        self
    }

    /// Prevent round robin selection of nameservers.
    pub fn set_no_rotate(&mut self) -> &mut Self {
        self.inner.set_no_rotate();
        self
    }

    /// Set the EDNS packet size.
    pub fn set_ednspsz(&mut self, size: u32) -> &mut Self {
        self.inner.set_ednspsz(size);
        self
    }
}

/// An asynchronous DNS resolver.
pub struct Resolver {
    ares_channel: Arc<Mutex<c_ares::Channel>>,
    event_loop_handle: EventLoopHandle,
}

impl Resolver {
    /// Create a new Resolver.
    pub fn new(options: Options) -> Result<Resolver, Error> {
        // Create and run the event loop.
        let event_loop = try!(EventLoop::new(options.inner));
        let channel = event_loop.ares_channel.clone();
        let handle = event_loop.run();

        // Return the Resolver.
        let resolver = Resolver {
            ares_channel: channel,
            event_loop_handle: handle,
        };
        Ok(resolver)
    }

    /// Set the list of servers to contact, instead of the servers specified
    /// in resolv.conf or the local named.
    ///
    /// String format is `host[:port]`.  IPv6 addresses with ports require
    /// square brackets eg `[2001:4860:4860::8888]:53`.
    pub fn set_servers(
        &mut self,
        servers: &[&str]) -> Result<&mut Self, c_ares::Error> {
        try!(self.ares_channel.lock().unwrap().set_servers(servers));
        Ok(self)
    }

    /// Set the local IPv4 address from which to make queries.
    pub fn set_local_ipv4(&mut self, ipv4: &Ipv4Addr) -> &mut Self {
        self.ares_channel.lock().unwrap().set_local_ipv4(ipv4);
        self
    }

    /// Set the local IPv6 address from which to make queries.
    pub fn set_local_ipv6(&mut self, ipv6: &Ipv6Addr) -> &mut Self {
        self.ares_channel.lock().unwrap().set_local_ipv6(ipv6);
        self
    }

    /// Set the local device from which to make queries.
    pub fn set_local_device(&mut self, device: &str) -> &mut Self {
        self.ares_channel.lock().unwrap().set_local_device(device);
        self
    }

    /// Look up the A records associated with `name`.
    pub fn query_a(&self, name: &str)
        -> futures::BoxFuture<c_ares::AResults, c_ares::Error> {
        let (c, p) = futures::oneshot();
        self.ares_channel.lock().unwrap().query_a(name, move |result| {
            c.complete(result);
        });
        p.map_err(|_| c_ares::Error::ECANCELLED)
            .and_then(futures::done)
            .boxed()
    }

    /// Look up the AAAA records associated with `name`.
    pub fn query_aaaa(&self, name: &str)
        -> futures::BoxFuture<c_ares::AAAAResults, c_ares::Error> {
        let (c, p) = futures::oneshot();
        self.ares_channel.lock().unwrap().query_aaaa(name, move |result| {
            c.complete(result);
        });
        p.map_err(|_| c_ares::Error::ECANCELLED)
            .and_then(futures::done)
            .boxed()
    }

    /// Look up the CNAME records associated with `name`.
    pub fn query_cname(&self, name: &str)
        -> futures::BoxFuture<c_ares::CNameResults, c_ares::Error> {
        let (c, p) = futures::oneshot();
        self.ares_channel.lock().unwrap().query_cname(name, move |result| {
            c.complete(result);
        });
        p.map_err(|_| c_ares::Error::ECANCELLED)
            .and_then(futures::done)
            .boxed()
    }

    /// Look up the MX records associated with `name`.
    pub fn query_mx(&self, name: &str)
        -> futures::BoxFuture<c_ares::MXResults, c_ares::Error> {
        let (c, p) = futures::oneshot();
        self.ares_channel.lock().unwrap().query_mx(name, move |result| {
            c.complete(result);
        });
        p.map_err(|_| c_ares::Error::ECANCELLED)
            .and_then(futures::done)
            .boxed()
    }

    /// Look up the NAPTR records associated with `name`.
    pub fn query_naptr(&self, name: &str)
        -> futures::BoxFuture<c_ares::NAPTRResults, c_ares::Error> {
        let (c, p) = futures::oneshot();
        self.ares_channel.lock().unwrap().query_naptr(name, move |result| {
            c.complete(result);
        });
        p.map_err(|_| c_ares::Error::ECANCELLED)
            .and_then(futures::done)
            .boxed()
    }

    /// Look up the NS records associated with `name`.
    pub fn query_ns(&self, name: &str)
        -> futures::BoxFuture<c_ares::NSResults, c_ares::Error> {
        let (c, p) = futures::oneshot();
        self.ares_channel.lock().unwrap().query_ns(name, move |result| {
            c.complete(result);
        });
        p.map_err(|_| c_ares::Error::ECANCELLED)
            .and_then(futures::done)
            .boxed()
    }

    /// Look up the PTR records associated with `name`.
    pub fn query_ptr(&self, name: &str)
        -> futures::BoxFuture<c_ares::PTRResults, c_ares::Error> {
        let (c, p) = futures::oneshot();
        self.ares_channel.lock().unwrap().query_ptr(name, move |result| {
            c.complete(result);
        });
        p.map_err(|_| c_ares::Error::ECANCELLED)
            .and_then(futures::done)
            .boxed()
    }

    /// Look up the SRV records associated with `name`.
    pub fn query_srv(&self, name: &str)
        -> futures::BoxFuture<c_ares::SRVResults, c_ares::Error> {
        let (c, p) = futures::oneshot();
        self.ares_channel.lock().unwrap().query_srv(name, move |result| {
            c.complete(result);
        });
        p.map_err(|_| c_ares::Error::ECANCELLED)
            .and_then(futures::done)
            .boxed()
    }

    /// Look up the TXT records associated with `name`.
    pub fn query_txt(&self, name: &str)
        -> futures::BoxFuture<c_ares::TXTResults, c_ares::Error> {
        let (c, p) = futures::oneshot();
        self.ares_channel.lock().unwrap().query_txt(name, move |result| {
            c.complete(result);
        });
        p.map_err(|_| c_ares::Error::ECANCELLED)
            .and_then(futures::done)
            .boxed()
    }

    /// Look up the SOA records associated with `name`.
    pub fn query_soa(&self, name: &str)
        -> futures::BoxFuture<c_ares::SOAResult, c_ares::Error> {
        let (c, p) = futures::oneshot();
        self.ares_channel.lock().unwrap().query_soa(name, move |result| {
            c.complete(result);
        });
        p.map_err(|_| c_ares::Error::ECANCELLED)
            .and_then(futures::done)
            .boxed()
    }

    /// Cancel all requests made on this `Channel`.
    pub fn cancel(&mut self) {
        self.ares_channel.lock().unwrap().cancel();
    }
}

impl Drop for Resolver {
    fn drop(&mut self) {
        // Shut down the event loop.
        self.event_loop_handle.shutdown();
    }
}
<|endoftext|>


<commit_before>
//! A NIF resource allows you to safely store rust structs in a term, and therefore keep it across
//! NIF calls. The struct will be automatically dropped when the BEAM GC decides that there are no
//! more references to the resource.

use std::mem;
use std::ptr;
use std::ops::Deref;
use std::marker::PhantomData;

use super::{ NifTerm, NifEnv, NifError, NifEncoder, NifDecoder, NifResult };
use ::wrapper::nif_interface::{ NIF_RESOURCE_TYPE, MUTABLE_NIF_RESOURCE_HANDLE, NIF_ENV, NifResourceFlags };
use ::wrapper::nif_interface::{ c_void };

/// Re-export a type used by the `resource_struct_init!` macro.
pub use ::wrapper::nif_interface::NIF_RESOURCE_FLAGS;

/// The NifResourceType struct contains a  NIF_RESOURCE_TYPE and a phantom reference to the type it
/// is for. It serves as a holder for the information needed to interact with the Erlang VM about
/// the resource type.
///
/// This is usually stored in an implementation of NifResourceTypeProvider.
#[doc(hidden)]
pub struct NifResourceType<T> {
    pub res: NIF_RESOURCE_TYPE,
    pub struct_type: PhantomData<T>,
}

/// This trait gets implemented for the type we want to put into a resource when
/// resource_struct_init! is called on it. It provides the NifResourceType.
///
/// In most cases the user should not have to worry about this.
#[doc(hidden)]
pub trait NifResourceTypeProvider: Sized + Send + Sync {
    fn get_type<'a>() -> &'a NifResourceType<Self>;
}

impl<T> NifEncoder for ResourceCell<T> where T: NifResourceTypeProvider {
    fn encode<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
        self.as_term(env)
    }
}
impl<'a, T> NifDecoder<'a> for ResourceCell<T> where T: NifResourceTypeProvider + 'a {
    fn decode(term: NifTerm<'a>) -> NifResult<Self> {
        ResourceCell::from_term(term)
    }
}

/// Drop a T that lives in an Erlang resource. (erlang_nif-sys requires us to declare this
/// function safe, but it is of course thoroughly unsafe!)
extern "C" fn resource_destructor<T>(_env: NIF_ENV, handle: MUTABLE_NIF_RESOURCE_HANDLE) {
    unsafe {
        let aligned = align_alloced_mem_for_struct::<T>(handle);
        let res = aligned as *mut T;
        ptr::read(res);
    }
}

/// This is the function that gets called from resource_struct_init! in on_load to create a new
/// resource type.
///
/// # Panics
///
/// Panics if `name` isn't null-terminated.
#[doc(hidden)]
pub fn open_struct_resource_type<'a, T: NifResourceTypeProvider>(env: NifEnv<'a>, name: &str,
                                 flags: NifResourceFlags) -> Option<NifResourceType<T>> {
    let res: Option<NIF_RESOURCE_TYPE> = unsafe {
        ::wrapper::resource::open_resource_type(env.as_c_arg(), name.as_bytes(), Some(resource_destructor::<T>), flags)
    };
    if res.is_some() {
        Some(NifResourceType {
            res: res.unwrap(),
            struct_type: PhantomData,
        })
    } else {
        None
    }
}

fn get_alloc_size_struct<T>() -> usize {
    mem::size_of::<T>() + mem::align_of::<T>()
}

/// Given a pointer `ptr` to an allocation of `get_alloc_size_struct::<T>()` bytes, return the
/// first aligned pointer within the allocation where a `T` may be stored.
/// Unsafe: `ptr` must point to a large enough allocation and not be null.
unsafe fn align_alloced_mem_for_struct<T>(ptr: *const c_void) -> *const c_void {
    let offset = mem::align_of::<T>() - ((ptr as usize) % mem::align_of::<T>());
    ptr.offset(offset as isize)
}

/// This is the struct that holds a reference to a resource. It increments the refcounter for the
/// resource instance on creation, and decrements when dropped.
pub struct ResourceCell<T> where T: NifResourceTypeProvider {
    raw: *const c_void,
    inner: *mut T,
}

impl<T> ResourceCell<T> where T: NifResourceTypeProvider {
    /// Makes a new ResourceCell from the given type. Note that the type must have
    /// NifResourceTypeProvider implemented for it. See module documentation for info on this.
    pub fn new(data: T) -> Self {
        let alloc_size = get_alloc_size_struct::<T>();
        let mem_raw = unsafe { ::wrapper::resource::alloc_resource(T::get_type().res, alloc_size) };
        let aligned_mem = unsafe { align_alloced_mem_for_struct::<T>(mem_raw) as *mut T };

        unsafe { ptr::write(aligned_mem, data) };

        ResourceCell {
            raw: mem_raw,
            inner: aligned_mem,
        }
    }

    fn from_term(term: NifTerm) -> Result<Self, NifError> {
        let res_resource = match unsafe { ::wrapper::resource::get_resource(term.get_env().as_c_arg(), term.as_c_arg(), T::get_type().res) } {
            Some(res) => res,
            None => return Err(NifError::BadArg),
        };
        unsafe { ::wrapper::resource::keep_resource(res_resource); }
        let casted_ptr = unsafe { align_alloced_mem_for_struct::<T>(res_resource) as *mut T };
        Ok(ResourceCell {
            raw: res_resource,
            inner: casted_ptr,
        })
    }

    fn as_term<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
        unsafe { NifTerm::new(env, ::wrapper::resource::make_resource(env.as_c_arg(), self.raw)) }
    }

    fn as_c_arg(&mut self) -> *const c_void {
        self.raw
    }

    fn inner(&self) -> &T {
        unsafe { &*self.inner }
    }
}

impl<T> Deref for ResourceCell<T> where T: NifResourceTypeProvider {
    type Target = T;

    fn deref(&self) -> &T {
        self.inner()
    }
}

impl<T> Clone for ResourceCell<T> where T: NifResourceTypeProvider {
    /// For a ResourceCell this will simply increment the refcounter for the resource instance and
    /// perform a fairly standard clone.
    fn clone(&self) -> Self {
        unsafe { ::wrapper::resource::keep_resource(self.raw); }
        ResourceCell {
            raw: self.raw,
            inner: self.inner,
        }
    }
}

impl<T> Drop for ResourceCell<T> where T: NifResourceTypeProvider {
    /// When drop is called for a ResourceCell, the reference held to the resource by the Cell is
    /// released.
    fn drop(&mut self) {
        unsafe { ::wrapper::nif_interface::enif_release_resource(self.as_c_arg()) };
    }
}

#[macro_export]
macro_rules! resource_struct_init {
    ($struct_name:ident, $env: ident) => {
        {
            static mut STRUCT_TYPE: Option<$crate::resource::NifResourceType<$struct_name>> = None;

            let temp_struct_type =
                match $crate::resource::open_struct_resource_type::<$struct_name>(
                    $env,
                    concat!(stringify!($struct_name), "\x00"),
                    $crate::resource::NIF_RESOURCE_FLAGS::ERL_NIF_RT_CREATE
                    ) {
                    Some(inner) => inner,
                    None => {
                        println!("Failure in creating resource type");
                        return false;
                    }
                };
            unsafe { STRUCT_TYPE = Some(temp_struct_type) };

            impl $crate::resource::NifResourceTypeProvider for $struct_name {
                fn get_type<'a>() -> &'a $crate::resource::NifResourceType<Self> {
                    unsafe { &STRUCT_TYPE }.as_ref().unwrap()
                }
            }
        }
    }
}
<commit_msg>Require resource types to have 'static lifetime.<commit_after>
//! A NIF resource allows you to safely store rust structs in a term, and therefore keep it across
//! NIF calls. The struct will be automatically dropped when the BEAM GC decides that there are no
//! more references to the resource.

use std::mem;
use std::ptr;
use std::ops::Deref;
use std::marker::PhantomData;

use super::{ NifTerm, NifEnv, NifError, NifEncoder, NifDecoder, NifResult };
use ::wrapper::nif_interface::{ NIF_RESOURCE_TYPE, MUTABLE_NIF_RESOURCE_HANDLE, NIF_ENV, NifResourceFlags };
use ::wrapper::nif_interface::{ c_void };

/// Re-export a type used by the `resource_struct_init!` macro.
pub use ::wrapper::nif_interface::NIF_RESOURCE_FLAGS;

/// The NifResourceType struct contains a  NIF_RESOURCE_TYPE and a phantom reference to the type it
/// is for. It serves as a holder for the information needed to interact with the Erlang VM about
/// the resource type.
///
/// This is usually stored in an implementation of NifResourceTypeProvider.
#[doc(hidden)]
pub struct NifResourceType<T> {
    pub res: NIF_RESOURCE_TYPE,
    pub struct_type: PhantomData<T>,
}

/// This trait gets implemented for the type we want to put into a resource when
/// resource_struct_init! is called on it. It provides the NifResourceType.
///
/// In most cases the user should not have to worry about this.
#[doc(hidden)]
pub trait NifResourceTypeProvider: Sized + Send + Sync + 'static {
    fn get_type() -> &'static NifResourceType<Self>;
}

impl<T> NifEncoder for ResourceCell<T> where T: NifResourceTypeProvider {
    fn encode<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
        self.as_term(env)
    }
}
impl<'a, T> NifDecoder<'a> for ResourceCell<T> where T: NifResourceTypeProvider + 'a {
    fn decode(term: NifTerm<'a>) -> NifResult<Self> {
        ResourceCell::from_term(term)
    }
}

/// Drop a T that lives in an Erlang resource. (erlang_nif-sys requires us to declare this
/// function safe, but it is of course thoroughly unsafe!)
extern "C" fn resource_destructor<T>(_env: NIF_ENV, handle: MUTABLE_NIF_RESOURCE_HANDLE) {
    unsafe {
        let aligned = align_alloced_mem_for_struct::<T>(handle);
        let res = aligned as *mut T;
        ptr::read(res);
    }
}

/// This is the function that gets called from resource_struct_init! in on_load to create a new
/// resource type.
///
/// # Panics
///
/// Panics if `name` isn't null-terminated.
#[doc(hidden)]
pub fn open_struct_resource_type<'a, T: NifResourceTypeProvider>(env: NifEnv<'a>, name: &str,
                                 flags: NifResourceFlags) -> Option<NifResourceType<T>> {
    let res: Option<NIF_RESOURCE_TYPE> = unsafe {
        ::wrapper::resource::open_resource_type(env.as_c_arg(), name.as_bytes(), Some(resource_destructor::<T>), flags)
    };
    if res.is_some() {
        Some(NifResourceType {
            res: res.unwrap(),
            struct_type: PhantomData,
        })
    } else {
        None
    }
}

fn get_alloc_size_struct<T>() -> usize {
    mem::size_of::<T>() + mem::align_of::<T>()
}

/// Given a pointer `ptr` to an allocation of `get_alloc_size_struct::<T>()` bytes, return the
/// first aligned pointer within the allocation where a `T` may be stored.
/// Unsafe: `ptr` must point to a large enough allocation and not be null.
unsafe fn align_alloced_mem_for_struct<T>(ptr: *const c_void) -> *const c_void {
    let offset = mem::align_of::<T>() - ((ptr as usize) % mem::align_of::<T>());
    ptr.offset(offset as isize)
}

/// This is the struct that holds a reference to a resource. It increments the refcounter for the
/// resource instance on creation, and decrements when dropped.
pub struct ResourceCell<T> where T: NifResourceTypeProvider {
    raw: *const c_void,
    inner: *mut T,
}

impl<T> ResourceCell<T> where T: NifResourceTypeProvider {
    /// Makes a new ResourceCell from the given type. Note that the type must have
    /// NifResourceTypeProvider implemented for it. See module documentation for info on this.
    pub fn new(data: T) -> Self {
        let alloc_size = get_alloc_size_struct::<T>();
        let mem_raw = unsafe { ::wrapper::resource::alloc_resource(T::get_type().res, alloc_size) };
        let aligned_mem = unsafe { align_alloced_mem_for_struct::<T>(mem_raw) as *mut T };

        unsafe { ptr::write(aligned_mem, data) };

        ResourceCell {
            raw: mem_raw,
            inner: aligned_mem,
        }
    }

    fn from_term(term: NifTerm) -> Result<Self, NifError> {
        let res_resource = match unsafe { ::wrapper::resource::get_resource(term.get_env().as_c_arg(), term.as_c_arg(), T::get_type().res) } {
            Some(res) => res,
            None => return Err(NifError::BadArg),
        };
        unsafe { ::wrapper::resource::keep_resource(res_resource); }
        let casted_ptr = unsafe { align_alloced_mem_for_struct::<T>(res_resource) as *mut T };
        Ok(ResourceCell {
            raw: res_resource,
            inner: casted_ptr,
        })
    }

    fn as_term<'a>(&self, env: NifEnv<'a>) -> NifTerm<'a> {
        unsafe { NifTerm::new(env, ::wrapper::resource::make_resource(env.as_c_arg(), self.raw)) }
    }

    fn as_c_arg(&mut self) -> *const c_void {
        self.raw
    }

    fn inner(&self) -> &T {
        unsafe { &*self.inner }
    }
}

impl<T> Deref for ResourceCell<T> where T: NifResourceTypeProvider {
    type Target = T;

    fn deref(&self) -> &T {
        self.inner()
    }
}

impl<T> Clone for ResourceCell<T> where T: NifResourceTypeProvider {
    /// For a ResourceCell this will simply increment the refcounter for the resource instance and
    /// perform a fairly standard clone.
    fn clone(&self) -> Self {
        unsafe { ::wrapper::resource::keep_resource(self.raw); }
        ResourceCell {
            raw: self.raw,
            inner: self.inner,
        }
    }
}

impl<T> Drop for ResourceCell<T> where T: NifResourceTypeProvider {
    /// When drop is called for a ResourceCell, the reference held to the resource by the Cell is
    /// released.
    fn drop(&mut self) {
        unsafe { ::wrapper::nif_interface::enif_release_resource(self.as_c_arg()) };
    }
}

#[macro_export]
macro_rules! resource_struct_init {
    ($struct_name:ident, $env: ident) => {
        {
            static mut STRUCT_TYPE: Option<$crate::resource::NifResourceType<$struct_name>> = None;

            let temp_struct_type =
                match $crate::resource::open_struct_resource_type::<$struct_name>(
                    $env,
                    concat!(stringify!($struct_name), "\x00"),
                    $crate::resource::NIF_RESOURCE_FLAGS::ERL_NIF_RT_CREATE
                    ) {
                    Some(inner) => inner,
                    None => {
                        println!("Failure in creating resource type");
                        return false;
                    }
                };
            unsafe { STRUCT_TYPE = Some(temp_struct_type) };

            impl $crate::resource::NifResourceTypeProvider for $struct_name {
                fn get_type() -> &'static $crate::resource::NifResourceType<Self> {
                    unsafe { &STRUCT_TYPE }.as_ref().unwrap()
                }
            }
        }
    }
}
<|endoftext|>