<commit_before>use crate::sys::config;
use crate::sys::taskserver::Control;
use super::SystemModule;
use anyhow::{Result, anyhow, ensure};
use chrono::NaiveTime;
use log::info;
use serde::{Serialize, Deserialize};
use tokio::process::Command;

#[derive(Clone, Serialize, Deserialize)]
struct HealthConfig {
    enabled: bool,
    debug_exec_once: bool,
}

pub struct Health {
    config: HealthConfig,
    wakeup_list: Vec<NaiveTime>,
}

impl Health {
    pub fn new(wakeup_list: Vec<NaiveTime>) -> Result<Self> {
        info!("[health] initialize");

        let jsobj = config::get_object(&["health"])
            .map_or(Err(anyhow!("Config not found: health")), Ok)?;
        let config: HealthConfig = serde_json::from_value(jsobj)?;

        Ok(Health {
            config,
            wakeup_list,
        })
    }

    async fn health_task(&mut self, ctrl: &Control) -> Result<()> {
        let cpu_info = get_cpu_info().await;
        info!("{:?}", cpu_info);
        let mem_info = get_mem_info().await;
        info!("{:?}", mem_info?);
        let disk_info = get_disk_info().await;
        info!("{:?}", disk_info?);

        Ok(())
    }

    async fn health_task_entry(ctrl: Control) -> Result<()> {
        let mut health = ctrl.sysmods().health.write().await;
        health.health_task(&ctrl).await
    }
}

impl SystemModule for Health {
    fn on_start(&self, ctrl: &Control) {
        info!("[health] on_start");
        if self.config.enabled {
            if self.config.debug_exec_once {
                ctrl.spawn_oneshot_task(
                    "health_check",
                    Health::health_task_entry);
            }
            else {
                ctrl.spawn_periodic_task(
                    "health_check",
                    &self.wakeup_list,
                    Health::health_task_entry);
            }
        }
    }
}

#[derive(Debug, Clone)]
struct CpuInfo {
    usage_percent_total: f64,
    usage_percent_list: Vec<f64>,
}

async fn get_cpu_info() -> Result<CpuInfo> {
    let buf = tokio::fs::read("/proc/stat").await?;
    let text = String::from_utf8_lossy(&buf);

    // See `man proc`
    // user   (1) Time spent in user mode.
    // nice   (2) Time spent in user mode with low priority (nice).
    // system (3) Time spent in system mode.
    // idle   (4) Time spent in the idle task.  This value should be USER_HZ times the second entry in the /proc/uptime pseudo-file.
    // iowait (since Linux 2.5.41)
    //        (5) Time waiting for I/O to complete.  This value is not reliable, for the following reasons:
    // irq (since Linux 2.6.0-test4)
    //        (6) Time servicing interrupts.
    // softirq (since Linux 2.6.0-test4)
    //        (7) Time servicing softirqs.
    // steal (since Linux 2.6.11)
    //        (8)  Stolen  time,  which  is the time spent in other operating systems when running in a virtualized environment
    // guest (since Linux 2.6.24)
    //        (9) Time spent running a virtual CPU for guest operating systems under the control of the Linux kernel.
    // guest_nice (since Linux 2.6.33)
    //        (10)  Time spent running a niced guest (virtual CPU for guest operating systems under the
    //        control of the Linux kernel).
    let mut usage_percent_total = None;
    let mut usage_percent_list = vec![];
    for line in text.lines() {
        let mut name = None;
        let mut user = None;
        let mut nice = None;
        let mut system = None;
        let mut idle = None;
        for (col_no, elem) in line.split_ascii_whitespace().enumerate() {
            match col_no {
                0 => name = Some(elem),
                1 => user = Some(elem),
                2 => nice = Some(elem),
                3 => system = Some(elem),
                4 => idle = Some(elem),
                _ => (),
            }
        }
        // cpu or cpu%d の行を取り出す
        if name.is_none() || !name.unwrap().starts_with("cpu") {
            continue;
        }

        let user: u64 = user.ok_or(anyhow!("parse error"))?.parse()?;
        let nice: u64 = nice.ok_or(anyhow!("parse error"))?.parse()?;
        let system: u64 = system.ok_or(anyhow!("parse error"))?.parse()?;
        let idle: u64 = idle.ok_or(anyhow!("parse error"))?.parse()?;
        let total = user + nice + system + idle;
        let value = (total - idle) as f64 / total as f64;
        if name == Some("cpu") {
            usage_percent_total = Some(value);
        }
        else {
            usage_percent_list.push(value);
        }
    }

    ensure!(usage_percent_total.is_some());
    ensure!(!usage_percent_list.is_empty());
    Ok(CpuInfo {
        usage_percent_total: usage_percent_total.unwrap(),
        usage_percent_list,
    })
}

#[derive(Debug, Clone, Copy)]
struct MemInfo {
    total_mib: f64,
    avail_mib: f64,
    percent: f64,
}

async fn get_mem_info() -> Result<MemInfo> {
    let mut cmd = Command::new("free");
	let output = cmd.output().await?;
    ensure!(output.status.success(), "free command failed");

    // sample
    //                total        used        free      shared  buff/cache   available
    // Mem:        13034888     4119272     5561960          68     3353656     8609008
    // Swap:        4194304           0     4194304
    let stdout = String::from_utf8_lossy(&output.stdout);
    let mut total = None;
    let mut avail = None;
    for (line_no, line) in stdout.lines().enumerate() {
        if line_no != 1 {
            continue;
        }
        for (col_no, elem) in line.split_ascii_whitespace().enumerate() {
            match col_no {
                1 => total = Some(elem),
                6 => avail = Some(elem),
                _ => (),
            }
        }
        break;
    }
    let total = total.ok_or(anyhow!("parse error"))?;
    let avail = avail.ok_or(anyhow!("parse error"))?;
    let total_mib = total.parse::<u64>()? as f64 / 1024.0;
    let avail_mib = avail.parse::<u64>()? as f64 / 1024.0;
    let percent = 100.0 * avail_mib / total_mib;

    Ok(MemInfo { total_mib, avail_mib, percent })
}

#[derive(Debug, Clone, Copy)]
struct DiskInfo {
    total_gib: f64,
    avail_gib: f64,
    percent: f64,
}

async fn get_disk_info() -> Result<DiskInfo> {
    let mut cmd = Command::new("df");
	let output = cmd.output().await?;
    ensure!(output.status.success(), "df command failed");

    // sample
    // ファイルシス   1K-ブロック     使用    使用可 使用% マウント位置
    // /dev/root        122621412 12964620 104641120   12% /
    // devtmpfs           1800568        0   1800568    0% /dev
    // tmpfs              1965432        0   1965432    0% /dev/shm
    // tmpfs              1965432    17116   1948316    1% /run
    // tmpfs                 5120        4      5116    1% /run/lock
    // tmpfs              1965432        0   1965432    0% /sys/fs/cgroup
    // /dev/mmcblk0p1      258095    49324    208772   20% /boot
    // /dev/sda1         59280316 57109344         0  100% /media/usbbkup
    let stdout = String::from_utf8_lossy(&output.stdout);
    let mut total = None;
    let mut avail = None;
    for line in stdout.lines().skip(1) {
        let mut total_tmp = None;
        let mut avail_tmp = None;
        let mut mp_tmp = None;
        for (col_no, elem) in line.split_ascii_whitespace().enumerate() {
            match col_no {
                1 => total_tmp = Some(elem),
                3 => avail_tmp = Some(elem),
                5 => mp_tmp = Some(elem),
                _ => (),
            }
        }
        if let Some(mp) = mp_tmp {
            if mp == "/" {
                total = total_tmp;
                avail = avail_tmp;
            }
        }
    }
    let total = total.ok_or(anyhow!("parse error"))?;
    let avail = avail.ok_or(anyhow!("parse error"))?;
    let total_gib = total.parse::<u64>()? as f64 / 1024.0 / 1024.0;
    let avail_gib = avail.parse::<u64>()? as f64 / 1024.0 / 1024.0;
    let percent = 100.0 * avail_gib / total_gib;

    Ok(DiskInfo { total_gib, avail_gib, percent })
}
<commit_msg>CPU 温度取得<commit_after>use crate::sys::config;
use crate::sys::taskserver::Control;
use super::SystemModule;
use anyhow::{Result, anyhow, ensure};
use chrono::NaiveTime;
use log::info;
use serde::{Serialize, Deserialize};
use tokio::process::Command;

#[derive(Clone, Serialize, Deserialize)]
struct HealthConfig {
    enabled: bool,
    debug_exec_once: bool,
}

pub struct Health {
    config: HealthConfig,
    wakeup_list: Vec<NaiveTime>,
}

impl Health {
    pub fn new(wakeup_list: Vec<NaiveTime>) -> Result<Self> {
        info!("[health] initialize");

        let jsobj = config::get_object(&["health"])
            .map_or(Err(anyhow!("Config not found: health")), Ok)?;
        let config: HealthConfig = serde_json::from_value(jsobj)?;

        Ok(Health {
            config,
            wakeup_list,
        })
    }

    async fn health_task(&mut self, ctrl: &Control) -> Result<()> {
        let cpu_info = get_cpu_info().await;
        info!("{:?}", cpu_info);
        let mem_info = get_mem_info().await;
        info!("{:?}", mem_info?);
        let disk_info = get_disk_info().await;
        info!("{:?}", disk_info?);
        let cpu_temp = get_cpu_temp().await;
        info!("{:?}", cpu_temp?);

        Ok(())
    }

    async fn health_task_entry(ctrl: Control) -> Result<()> {
        let mut health = ctrl.sysmods().health.write().await;
        health.health_task(&ctrl).await
    }
}

impl SystemModule for Health {
    fn on_start(&self, ctrl: &Control) {
        info!("[health] on_start");
        if self.config.enabled {
            if self.config.debug_exec_once {
                ctrl.spawn_oneshot_task(
                    "health_check",
                    Health::health_task_entry);
            }
            else {
                ctrl.spawn_periodic_task(
                    "health_check",
                    &self.wakeup_list,
                    Health::health_task_entry);
            }
        }
    }
}

#[derive(Debug, Clone)]
struct CpuInfo {
    usage_percent_total: f64,
    usage_percent_list: Vec<f64>,
}

async fn get_cpu_info() -> Result<CpuInfo> {
    let buf = tokio::fs::read("/proc/stat").await?;
    let text = String::from_utf8_lossy(&buf);

    // See `man proc`
    // user   (1) Time spent in user mode.
    // nice   (2) Time spent in user mode with low priority (nice).
    // system (3) Time spent in system mode.
    // idle   (4) Time spent in the idle task.  This value should be USER_HZ times the second entry in the /proc/uptime pseudo-file.
    // iowait (since Linux 2.5.41)
    //        (5) Time waiting for I/O to complete.  This value is not reliable, for the following reasons:
    // irq (since Linux 2.6.0-test4)
    //        (6) Time servicing interrupts.
    // softirq (since Linux 2.6.0-test4)
    //        (7) Time servicing softirqs.
    // steal (since Linux 2.6.11)
    //        (8)  Stolen  time,  which  is the time spent in other operating systems when running in a virtualized environment
    // guest (since Linux 2.6.24)
    //        (9) Time spent running a virtual CPU for guest operating systems under the control of the Linux kernel.
    // guest_nice (since Linux 2.6.33)
    //        (10)  Time spent running a niced guest (virtual CPU for guest operating systems under the
    //        control of the Linux kernel).
    let mut usage_percent_total = None;
    let mut usage_percent_list = vec![];
    for line in text.lines() {
        let mut name = None;
        let mut user = None;
        let mut nice = None;
        let mut system = None;
        let mut idle = None;
        for (col_no, elem) in line.split_ascii_whitespace().enumerate() {
            match col_no {
                0 => name = Some(elem),
                1 => user = Some(elem),
                2 => nice = Some(elem),
                3 => system = Some(elem),
                4 => idle = Some(elem),
                _ => (),
            }
        }
        // cpu or cpu%d の行を取り出す
        if name.is_none() || !name.unwrap().starts_with("cpu") {
            continue;
        }

        let user: u64 = user.ok_or(anyhow!("parse error"))?.parse()?;
        let nice: u64 = nice.ok_or(anyhow!("parse error"))?.parse()?;
        let system: u64 = system.ok_or(anyhow!("parse error"))?.parse()?;
        let idle: u64 = idle.ok_or(anyhow!("parse error"))?.parse()?;
        let total = user + nice + system + idle;
        let value = (total - idle) as f64 / total as f64;
        if name == Some("cpu") {
            usage_percent_total = Some(value);
        }
        else {
            usage_percent_list.push(value);
        }
    }

    ensure!(usage_percent_total.is_some());
    ensure!(!usage_percent_list.is_empty());
    Ok(CpuInfo {
        usage_percent_total: usage_percent_total.unwrap(),
        usage_percent_list,
    })
}

#[derive(Debug, Clone, Copy)]
struct MemInfo {
    total_mib: f64,
    avail_mib: f64,
    percent: f64,
}

async fn get_mem_info() -> Result<MemInfo> {
    let mut cmd = Command::new("free");
	let output = cmd.output().await?;
    ensure!(output.status.success(), "free command failed");

    // sample
    //                total        used        free      shared  buff/cache   available
    // Mem:        13034888     4119272     5561960          68     3353656     8609008
    // Swap:        4194304           0     4194304
    let stdout = String::from_utf8_lossy(&output.stdout);
    let mut total = None;
    let mut avail = None;
    for (line_no, line) in stdout.lines().enumerate() {
        if line_no != 1 {
            continue;
        }
        for (col_no, elem) in line.split_ascii_whitespace().enumerate() {
            match col_no {
                1 => total = Some(elem),
                6 => avail = Some(elem),
                _ => (),
            }
        }
        break;
    }
    let total = total.ok_or(anyhow!("parse error"))?;
    let avail = avail.ok_or(anyhow!("parse error"))?;
    let total_mib = total.parse::<u64>()? as f64 / 1024.0;
    let avail_mib = avail.parse::<u64>()? as f64 / 1024.0;
    let percent = 100.0 * avail_mib / total_mib;

    Ok(MemInfo { total_mib, avail_mib, percent })
}

#[derive(Debug, Clone, Copy)]
struct DiskInfo {
    total_gib: f64,
    avail_gib: f64,
    percent: f64,
}

async fn get_disk_info() -> Result<DiskInfo> {
    let mut cmd = Command::new("df");
	let output = cmd.output().await?;
    ensure!(output.status.success(), "df command failed");

    // sample
    // ファイルシス   1K-ブロック     使用    使用可 使用% マウント位置
    // /dev/root        122621412 12964620 104641120   12% /
    // devtmpfs           1800568        0   1800568    0% /dev
    // tmpfs              1965432        0   1965432    0% /dev/shm
    // tmpfs              1965432    17116   1948316    1% /run
    // tmpfs                 5120        4      5116    1% /run/lock
    // tmpfs              1965432        0   1965432    0% /sys/fs/cgroup
    // /dev/mmcblk0p1      258095    49324    208772   20% /boot
    // /dev/sda1         59280316 57109344         0  100% /media/usbbkup
    let stdout = String::from_utf8_lossy(&output.stdout);
    let mut total = None;
    let mut avail = None;
    for line in stdout.lines().skip(1) {
        let mut total_tmp = None;
        let mut avail_tmp = None;
        let mut mp_tmp = None;
        for (col_no, elem) in line.split_ascii_whitespace().enumerate() {
            match col_no {
                1 => total_tmp = Some(elem),
                3 => avail_tmp = Some(elem),
                5 => mp_tmp = Some(elem),
                _ => (),
            }
        }
        if let Some(mp) = mp_tmp {
            if mp == "/" {
                total = total_tmp;
                avail = avail_tmp;
            }
        }
    }
    let total = total.ok_or(anyhow!("parse error"))?;
    let avail = avail.ok_or(anyhow!("parse error"))?;
    let total_gib = total.parse::<u64>()? as f64 / 1024.0 / 1024.0;
    let avail_gib = avail.parse::<u64>()? as f64 / 1024.0 / 1024.0;
    let percent = 100.0 * avail_gib / total_gib;

    Ok(DiskInfo { total_gib, avail_gib, percent })
}

#[derive(Debug, Clone, Copy)]
struct CpuTemp {
    temp: f64,
}

/// CPU 温度 (正確には違うかもしれない。ボード上の何らかの温度センサの値。) を取得する。
///
/// "/sys/class/thermal/thermal_zone0/temp" による。
/// デバイスファイルが存在しないと [std::io::Error] (NotFound) になる。
/// Linux 汎用のようだが少なくとも WSL2 では存在しない。
/// RasPi only で `vcgencmd measure_temp` という手もあるが、
/// 人が読みやすい代わりにパースが難しくなるのでデバイスファイルの方を使う。
async fn get_cpu_temp() -> Result<CpuTemp> {
    let buf = tokio::fs::read("/sys/class/thermal/thermal_zone0/temp").await?;
    let text = String::from_utf8_lossy(&buf);

    // 'C を 1000 倍した整数が得られるので変換する
    let temp: u64 = text.trim().parse()?;
    let temp = temp as f64 / 1000.0;

    Ok(CpuTemp { temp })
}
<|endoftext|>


<commit_before>use cgmath::Point2;
use ggez::event::{KeyCode, KeyMods};
use ggez::graphics::{self, Canvas, DrawParam, Image};
use ggez::{Context, GameResult};
use map::{map_id_to_facet, Facet, MAP_DETAILS};
use scene::{BoxedScene, Scene, SceneChangeEvent, SceneName};
use std::io::Result;
use std::path::Path;
use uorustlibs::color::{Color as ColorTrait, Color16};
use uorustlibs::map::{Block, RadarColReader, StaticLocation};

const MAX_BLOCKS_WIDTH: u32 = 800 / 8;
const MAX_BLOCKS_HEIGHT: u32 = 600 / 8;
const STEP_X: u32 = MAX_BLOCKS_WIDTH / 4;
const STEP_Y: u32 = MAX_BLOCKS_HEIGHT / 4;

enum MapRenderMode {
    HeightMap,
    RadarMap,
    StaticsMap,
}

pub struct MapScene {
    facet: Facet,
    map_id: u8,
    radar_colors: Result<Vec<Color16>>,
    mode: MapRenderMode,
    texture: Option<Canvas>,
    exiting: bool,
}

pub fn draw_heightmap_block(
    ctx: &mut Context,
    block: &Block,
    _statics: &Vec<StaticLocation>,
    _radar_cols: &Result<Vec<Color16>>,
) -> Image {
    let mut bitmap = vec![0; 8 * 8 * 4];
    for y in 0..8 {
        for x in 0..8 {
            let target = x + (y * 8);
            let height = (block.cells[target].altitude as i16 + 128) as u8;
            bitmap[target * 4] = height;
            bitmap[target * 4 + 1] = height;
            bitmap[target * 4 + 2] = height;
            bitmap[target * 4 + 3] = 255;
        }
    }
    Image::from_rgba8(ctx, 8, 8, &bitmap).expect("Failed to create surface")
}

pub fn draw_radarcol_block(
    ctx: &mut Context,
    block: &Block,
    _statics: &Vec<StaticLocation>,
    radar_cols: &Result<Vec<Color16>>,
) -> Image {
    let mut bitmap = vec![0; 8 * 8 * 4];
    for y in 0..8 {
        for x in 0..8 {
            let target = x + (y * 8);
            let index = block.cells[target].graphic;
            let (r, g, b, _) = match radar_cols {
                &Ok(ref colors) => colors[index as usize].to_rgba(),
                _ => index.to_rgba(),
            };
            bitmap[target * 4] = r;
            bitmap[target * 4 + 1] = g;
            bitmap[target * 4 + 2] = b;
            bitmap[target * 4 + 3] = 255;
        }
    }
    Image::from_rgba8(ctx, 8, 8, &bitmap).expect("Failed to create surface")
}

pub fn draw_statics_block(
    ctx: &mut Context,
    _block: &Block,
    statics: &Vec<StaticLocation>,
    radar_cols: &Result<Vec<Color16>>,
) -> Image {
    let mut bitmap = vec![0; 8 * 8 * 4];
    let mut last_height_locs = vec![-127; 64];
    for stat in statics {
        let lookup = (stat.x + (stat.y * 8)) as usize;
        if last_height_locs[lookup] < stat.altitude {
            // Paint the cell as we're higher.
            // Inefficient, but probably no worse than trying to keep track of which items are in which cell
            let (r, g, b, _) = match radar_cols {
                &Ok(ref colors) => colors[stat.color_idx() as usize].to_rgba(),
                _ => (0, 0, 0, 0),
            };
            bitmap[lookup * 4] = r;
            bitmap[lookup * 4 + 1] = g;
            bitmap[lookup * 4 + 2] = b;
            bitmap[lookup * 4 + 3] = 255;

            last_height_locs[lookup] = stat.altitude;
        }
    }
    Image::from_rgba8(ctx, 8, 8, &bitmap).expect("Failed to create surface")
}

impl<'a> MapScene {
    pub fn new(ctx: &mut Context) -> BoxedScene<'a, SceneName, ()> {
        let colors = RadarColReader::new(&Path::new("./assets/radarcol.mul"))
            .and_then(|mut reader| reader.read_colors());

        let mut scene = Box::new(MapScene {
            facet: map_id_to_facet(0),
            map_id: 0,
            texture: None,
            mode: MapRenderMode::HeightMap,
            radar_colors: colors,
            exiting: false,
        });

        scene.draw_page(ctx).expect("Failed to draw map");
        scene
    }

    pub fn draw_page(&mut self, ctx: &mut Context) -> GameResult<()> {
        let lens = self.facet.get_lens(MAX_BLOCKS_WIDTH, MAX_BLOCKS_HEIGHT);

        self.texture = match lens {
            Some(lens) => {
                let dest = Canvas::with_window_size(ctx)?;
                graphics::set_canvas(ctx, Some(&dest));
                graphics::clear(ctx, graphics::BLACK);

                let block_drawer = match self.mode {
                    MapRenderMode::HeightMap => draw_heightmap_block,
                    MapRenderMode::RadarMap => draw_radarcol_block,
                    MapRenderMode::StaticsMap => draw_statics_block,
                };
                for y in 0..lens.height {
                    for x in 0..lens.width {
                        match &lens.blocks[(x + (y * MAX_BLOCKS_WIDTH)) as usize] {
                            &(Some(ref block), Some(ref statics)) => {
                                let block_surface =
                                    block_drawer(ctx, block, statics, &self.radar_colors);
                                graphics::draw(
                                    ctx,
                                    &block_surface,
                                    DrawParam::default()
                                        .dest(Point2::new(x as f32 * 8.0, y as f32 * 8.0)),
                                )?;
                            }
                            &(Some(ref block), _) => {
                                let block_surface =
                                    block_drawer(ctx, block, &vec![], &self.radar_colors);
                                graphics::draw(
                                    ctx,
                                    &block_surface,
                                    DrawParam::default()
                                        .dest(Point2::new(x as f32 * 8.0, y as f32 * 8.0)),
                                )?;
                            }
                            _ => (),
                        }
                    }
                }
                graphics::set_canvas(ctx, None);
                Some(dest)
            }
            None => None,
        };
        Ok(())
    }
}

impl Scene<SceneName, ()> for MapScene {
    fn draw(&mut self, ctx: &mut Context, _engine_data: &mut ()) -> GameResult<()> {
        match self.texture {
            Some(ref texture) => {
                graphics::draw(ctx, texture, DrawParam::default())?;
            }
            None => (),
        };
        Ok(())
    }

    fn update(
        &mut self,
        _ctx: &mut Context,
        _engine_data: &mut (),
    ) -> GameResult<Option<SceneChangeEvent<SceneName>>> {
        if self.exiting {
            Ok(Some(SceneChangeEvent::PopScene))
        } else {
            Ok(None)
        }
    }

    fn key_down_event(
        &mut self,
        ctx: &mut Context,
        keycode: KeyCode,
        _keymods: KeyMods,
        _repeat: bool,
        _engine_data: &mut (),
    ) {
        match keycode {
            KeyCode::Escape => self.exiting = true,
            KeyCode::Left => {
                if self.facet.x >= STEP_X as u32 {
                    self.facet.x -= STEP_X as u32;
                    self.draw_page(ctx).expect("Failed to draw map");
                }
            }
            KeyCode::Right => {
                self.facet.x += STEP_X as u32;
                self.draw_page(ctx).expect("Failed to draw map");
            }
            KeyCode::Up => {
                if self.facet.y >= STEP_Y as u32 {
                    self.facet.y -= STEP_Y as u32;
                    self.draw_page(ctx).expect("Failed to draw map");
                }
            }
            KeyCode::Down => {
                self.facet.y += STEP_Y as u32;
                self.draw_page(ctx).expect("Failed to draw map");
            }
            KeyCode::Key1 => {
                self.mode = MapRenderMode::HeightMap;
                self.draw_page(ctx).expect("Failed to draw map");
            }
            KeyCode::Key2 => {
                self.mode = MapRenderMode::RadarMap;
                self.draw_page(ctx).expect("Failed to draw map");
            }
            KeyCode::Key3 => {
                self.mode = MapRenderMode::StaticsMap;
                self.draw_page(ctx).expect("Failed to draw map");
            }
            KeyCode::Tab => {
                self.mode = MapRenderMode::HeightMap;
                self.map_id = (self.map_id + 1) % MAP_DETAILS.len() as u8;
                self.facet = map_id_to_facet(self.map_id);
                self.draw_page(ctx).expect("Failed to draw map");
            }
            _ => (),
        }
    }
}
<commit_msg>Added mixed map mode<commit_after>use cgmath::Point2;
use ggez::event::{KeyCode, KeyMods};
use ggez::graphics::{self, Canvas, DrawParam, Image};
use ggez::{Context, GameResult};
use map::{map_id_to_facet, Facet, MAP_DETAILS};
use scene::{BoxedScene, Scene, SceneChangeEvent, SceneName};
use std::io::Result;
use std::path::Path;
use uorustlibs::color::{Color as ColorTrait, Color16};
use uorustlibs::map::{Block, RadarColReader, StaticLocation};

const MAX_BLOCKS_WIDTH: u32 = 800 / 8;
const MAX_BLOCKS_HEIGHT: u32 = 600 / 8;
const STEP_X: u32 = MAX_BLOCKS_WIDTH / 4;
const STEP_Y: u32 = MAX_BLOCKS_HEIGHT / 4;

enum MapRenderMode {
    HeightMap,
    RadarMap,
    StaticsMap,
    FullMap,
}

pub struct MapScene {
    facet: Facet,
    map_id: u8,
    radar_colors: Result<Vec<Color16>>,
    mode: MapRenderMode,
    texture: Option<Canvas>,
    exiting: bool,
}

pub fn draw_heightmap_block(
    bitmap: &mut Vec<u8>,
    block: &Block,
    _statics: &Vec<StaticLocation>,
    _radar_cols: &Result<Vec<Color16>>,
) {
    for y in 0..8 {
        for x in 0..8 {
            let target = x + (y * 8);
            let height = (block.cells[target].altitude as i16 + 128) as u8;
            bitmap[target * 4] = height;
            bitmap[target * 4 + 1] = height;
            bitmap[target * 4 + 2] = height;
            bitmap[target * 4 + 3] = 255;
        }
    }
}

pub fn draw_radarcol_block(
    bitmap: &mut Vec<u8>,
    block: &Block,
    _statics: &Vec<StaticLocation>,
    radar_cols: &Result<Vec<Color16>>,
) {
    for y in 0..8 {
        for x in 0..8 {
            let target = x + (y * 8);
            let index = block.cells[target].graphic;
            let (r, g, b, _) = match radar_cols {
                &Ok(ref colors) => colors[index as usize].to_rgba(),
                _ => index.to_rgba(),
            };
            bitmap[target * 4] = r;
            bitmap[target * 4 + 1] = g;
            bitmap[target * 4 + 2] = b;
            bitmap[target * 4 + 3] = 255;
        }
    }
}

pub fn draw_statics_block(
    bitmap: &mut Vec<u8>,
    _block: &Block,
    statics: &Vec<StaticLocation>,
    radar_cols: &Result<Vec<Color16>>,
) {
    let mut last_height_locs = vec![-127; 64];
    for stat in statics {
        let lookup = (stat.x + (stat.y * 8)) as usize;
        if last_height_locs[lookup] < stat.altitude {
            // Paint the cell as we're higher.
            // Inefficient, but probably no worse than trying to keep track of which items are in which cell
            let (r, g, b, _) = match radar_cols {
                &Ok(ref colors) => colors[stat.color_idx() as usize].to_rgba(),
                _ => (0, 0, 0, 0),
            };
            bitmap[lookup * 4] = r;
            bitmap[lookup * 4 + 1] = g;
            bitmap[lookup * 4 + 2] = b;
            bitmap[lookup * 4 + 3] = 255;

            last_height_locs[lookup] = stat.altitude;
        }
    }
}

pub fn draw_full_block(
    bitmap: &mut Vec<u8>,
    block: &Block,
    statics: &Vec<StaticLocation>,
    radar_cols: &Result<Vec<Color16>>,
) {
    draw_radarcol_block(bitmap, block, statics, radar_cols);
    draw_statics_block(bitmap, block, statics, radar_cols);
}

impl<'a> MapScene {
    pub fn new(ctx: &mut Context) -> BoxedScene<'a, SceneName, ()> {
        let colors = RadarColReader::new(&Path::new("./assets/radarcol.mul"))
            .and_then(|mut reader| reader.read_colors());

        let mut scene = Box::new(MapScene {
            facet: map_id_to_facet(0),
            map_id: 0,
            texture: None,
            mode: MapRenderMode::HeightMap,
            radar_colors: colors,
            exiting: false,
        });

        scene.draw_page(ctx).expect("Failed to draw map");
        scene
    }

    pub fn draw_page(&mut self, ctx: &mut Context) -> GameResult<()> {
        let lens = self.facet.get_lens(MAX_BLOCKS_WIDTH, MAX_BLOCKS_HEIGHT);

        self.texture = match lens {
            Some(lens) => {
                let dest = Canvas::with_window_size(ctx)?;
                graphics::set_canvas(ctx, Some(&dest));
                graphics::clear(ctx, graphics::BLACK);

                let block_drawer = match self.mode {
                    MapRenderMode::HeightMap => draw_heightmap_block,
                    MapRenderMode::RadarMap => draw_radarcol_block,
                    MapRenderMode::StaticsMap => draw_statics_block,
                    MapRenderMode::FullMap => draw_full_block,
                };
                for y in 0..lens.height {
                    for x in 0..lens.width {
                        match &lens.blocks[(x + (y * MAX_BLOCKS_WIDTH)) as usize] {
                            &(Some(ref block), Some(ref statics)) => {
                                let mut bitmap = vec![0; 8 * 8 * 4];
                                block_drawer(&mut bitmap, block, statics, &self.radar_colors);
                                let block_surface = Image::from_rgba8(ctx, 8, 8, &bitmap)
                                    .expect("Failed to create surface");
                                graphics::draw(
                                    ctx,
                                    &block_surface,
                                    DrawParam::default()
                                        .dest(Point2::new(x as f32 * 8.0, y as f32 * 8.0)),
                                )?;
                            }
                            &(Some(ref block), _) => {
                                let mut bitmap = vec![0; 8 * 8 * 4];
                                block_drawer(&mut bitmap, block, &vec![], &self.radar_colors);
                                let block_surface = Image::from_rgba8(ctx, 8, 8, &bitmap)
                                    .expect("Failed to create surface");
                                graphics::draw(
                                    ctx,
                                    &block_surface,
                                    DrawParam::default()
                                        .dest(Point2::new(x as f32 * 8.0, y as f32 * 8.0)),
                                )?;
                            }
                            _ => (),
                        }
                    }
                }
                graphics::set_canvas(ctx, None);
                Some(dest)
            }
            None => None,
        };
        Ok(())
    }
}

impl Scene<SceneName, ()> for MapScene {
    fn draw(&mut self, ctx: &mut Context, _engine_data: &mut ()) -> GameResult<()> {
        match self.texture {
            Some(ref texture) => {
                graphics::draw(ctx, texture, DrawParam::default())?;
            }
            None => (),
        };
        Ok(())
    }

    fn update(
        &mut self,
        _ctx: &mut Context,
        _engine_data: &mut (),
    ) -> GameResult<Option<SceneChangeEvent<SceneName>>> {
        if self.exiting {
            Ok(Some(SceneChangeEvent::PopScene))
        } else {
            Ok(None)
        }
    }

    fn key_down_event(
        &mut self,
        ctx: &mut Context,
        keycode: KeyCode,
        _keymods: KeyMods,
        _repeat: bool,
        _engine_data: &mut (),
    ) {
        match keycode {
            KeyCode::Escape => self.exiting = true,
            KeyCode::Left => {
                if self.facet.x >= STEP_X as u32 {
                    self.facet.x -= STEP_X as u32;
                    self.draw_page(ctx).expect("Failed to draw map");
                }
            }
            KeyCode::Right => {
                self.facet.x += STEP_X as u32;
                self.draw_page(ctx).expect("Failed to draw map");
            }
            KeyCode::Up => {
                if self.facet.y >= STEP_Y as u32 {
                    self.facet.y -= STEP_Y as u32;
                    self.draw_page(ctx).expect("Failed to draw map");
                }
            }
            KeyCode::Down => {
                self.facet.y += STEP_Y as u32;
                self.draw_page(ctx).expect("Failed to draw map");
            }
            KeyCode::Key1 => {
                self.mode = MapRenderMode::HeightMap;
                self.draw_page(ctx).expect("Failed to draw map");
            }
            KeyCode::Key2 => {
                self.mode = MapRenderMode::RadarMap;
                self.draw_page(ctx).expect("Failed to draw map");
            }
            KeyCode::Key3 => {
                self.mode = MapRenderMode::StaticsMap;
                self.draw_page(ctx).expect("Failed to draw map");
            }
            KeyCode::Key4 => {
                self.mode = MapRenderMode::FullMap;
                self.draw_page(ctx).expect("Failed to draw map");
            }
            KeyCode::Tab => {
                self.mode = MapRenderMode::HeightMap;
                self.map_id = (self.map_id + 1) % MAP_DETAILS.len() as u8;
                self.facet = map_id_to_facet(self.map_id);
                self.draw_page(ctx).expect("Failed to draw map");
            }
            _ => (),
        }
    }
}
<|endoftext|>


<commit_before>pub mod torrent;
pub mod peer;
pub mod scrape;
pub mod announce;
pub mod stats;

use self::torrent::Torrent;
use self::announce::{AnnounceResponse, Announce};
use self::scrape::{ScrapeResponse, Scrape};
use self::stats::{Stats, StatsResponse};
use response::error::ErrorResponse;
use response::success::SuccessResponse;
use private::PrivateTracker;

use std::sync::{Mutex, MutexGuard};
use std::collections::HashMap;
use time::SteadyTime;
use time::Duration;

pub struct Tracker {
    pub torrents: Mutex<HashMap<String, Torrent>>,
    pub stats: Mutex<Stats>,
    pub private: PrivateTracker,
}

impl Tracker {
    pub fn new() -> Tracker {
        let torrents: Mutex<HashMap<String, Torrent>> = Mutex::new(Default::default());
        let stats = Mutex::new(Stats::new());
        let private = PrivateTracker::new();
        Tracker {
            torrents: torrents,
            stats: stats,
            private: private
        }
    }

    fn unlock_stats(&self) -> MutexGuard<Stats> {
        match self.stats.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        }
    }

    fn unlock_torrents(&self) -> MutexGuard<HashMap<String, Torrent>> {
        match self.torrents.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        }
    }

    pub fn handle_announce(&self, announce: Announce) -> Result<SuccessResponse, ErrorResponse> {
        let mut torrents = self.unlock_torrents();
        let mut tracker_stats = self.unlock_stats();
        let torrent = if torrents.contains_key(&announce.info_hash) {
            let t = torrents.get_mut(&announce.info_hash).unwrap();
            tracker_stats.announces += 1;
            tracker_stats.peers -= t.get_peer_count();
            let delta = t.update(&announce);
            if cfg!(feature = "private") {
                self.private.add_announce(delta);
            }
            tracker_stats.peers += t.get_peer_count();
            t
        } else {
            tracker_stats.torrents += 1;
            tracker_stats.announces += 1;
            tracker_stats.peers += 1;

            let t = Torrent::new(announce.info_hash.clone());
            torrents.insert(announce.info_hash.clone(), t);
            let t = torrents.get_mut(&announce.info_hash).unwrap();
            let delta = t.update(&announce);
            if cfg!(feature = "private") {
                self.private.add_announce(delta);
            }
            t
        };
        Ok(SuccessResponse::Announce(AnnounceResponse::new(torrent.get_peers(announce.numwant, announce.action), torrent.get_stats(), announce.compact)))
    }

    pub fn handle_scrape(&self, scrape: Scrape) -> Result<SuccessResponse, ErrorResponse> {
        let mut torrents = HashMap::new();
        for hash in scrape.torrents {
            match self.unlock_torrents().get(&hash) {
                Some(ref t) => {
                    let stats = t.get_stats();
                    torrents.insert(hash.clone(), stats);
                }
                None => {}
            };
        }

        let mut tracker_stats = self.unlock_stats();
        tracker_stats.scrapes += 1;

        Ok(SuccessResponse::Scrape(ScrapeResponse { torrents: torrents }))
    }

    pub fn get_stats(&self) -> Result<SuccessResponse, ErrorResponse> {
        let ref stats = *self.unlock_stats();
        let resp = StatsResponse::new(stats);
        Ok(SuccessResponse::Stats(resp))
    }

    pub fn reap(&self) {
        // Clear stats
        let mut stats = self.unlock_stats();
        stats.update();
        // Delete torrents which are too old, and reap peers for the others.
        let to_del: Vec<_> = self.unlock_torrents()
            .iter()
            .filter_map(|(k, torrent)| {
                if SteadyTime::now() - torrent.last_action >
                    Duration::seconds(3600) {
                        Some(k.clone())
                    } else {
                        None
                    }
            })
        .collect();
        for torrent in to_del {
            stats.torrents -= 1;
            self.unlock_torrents().remove(&torrent);
        }

        let to_reap: Vec<_> = self.unlock_torrents()
            .iter()
            .filter_map(|(k, torrent)| {
                if SteadyTime::now() - torrent.last_action >
                    Duration::seconds(3600) {
                        None
                    } else {
                        Some(k.clone())
                    }
            })
        .collect();
        stats.peers = 0;
        for info_hash in to_reap {
            match self.unlock_torrents().get_mut(&info_hash) {
                Some(ref mut t) => {
                    t.reap();
                    stats.peers += t.get_peer_count();
                }
                None => {}
            }
        }
    }
}
<commit_msg>Minor tweak to make return look better<commit_after>pub mod torrent;
pub mod peer;
pub mod scrape;
pub mod announce;
pub mod stats;

use self::torrent::Torrent;
use self::announce::{AnnounceResponse, Announce};
use self::scrape::{ScrapeResponse, Scrape};
use self::stats::{Stats, StatsResponse};
use response::error::ErrorResponse;
use response::success::SuccessResponse;
use private::PrivateTracker;

use std::sync::{Mutex, MutexGuard};
use std::collections::HashMap;
use time::SteadyTime;
use time::Duration;

pub struct Tracker {
    pub torrents: Mutex<HashMap<String, Torrent>>,
    pub stats: Mutex<Stats>,
    pub private: PrivateTracker,
}

impl Tracker {
    pub fn new() -> Tracker {
        let torrents: Mutex<HashMap<String, Torrent>> = Mutex::new(Default::default());
        let stats = Mutex::new(Stats::new());
        let private = PrivateTracker::new();
        Tracker {
            torrents: torrents,
            stats: stats,
            private: private
        }
    }

    fn unlock_stats(&self) -> MutexGuard<Stats> {
        match self.stats.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        }
    }

    fn unlock_torrents(&self) -> MutexGuard<HashMap<String, Torrent>> {
        match self.torrents.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        }
    }

    pub fn handle_announce(&self, announce: Announce) -> Result<SuccessResponse, ErrorResponse> {
        let mut torrents = self.unlock_torrents();
        let mut tracker_stats = self.unlock_stats();
        let torrent = if torrents.contains_key(&announce.info_hash) {
            let t = torrents.get_mut(&announce.info_hash).unwrap();
            tracker_stats.announces += 1;
            tracker_stats.peers -= t.get_peer_count();
            let delta = t.update(&announce);
            if cfg!(feature = "private") {
                self.private.add_announce(delta);
            }
            tracker_stats.peers += t.get_peer_count();
            t
        } else {
            tracker_stats.torrents += 1;
            tracker_stats.announces += 1;
            tracker_stats.peers += 1;

            let t = Torrent::new(announce.info_hash.clone());
            torrents.insert(announce.info_hash.clone(), t);
            let t = torrents.get_mut(&announce.info_hash).unwrap();
            let delta = t.update(&announce);
            if cfg!(feature = "private") {
                self.private.add_announce(delta);
            }
            t
        };
        let peers = torrent.get_peers(announce.numwant, announce.action);
        let stats = torrent.get_stats();
        Ok(SuccessResponse::Announce(AnnounceResponse::new(peers, stats, announce.compact)))
    }

    pub fn handle_scrape(&self, scrape: Scrape) -> Result<SuccessResponse, ErrorResponse> {
        let mut torrents = HashMap::new();
        for hash in scrape.torrents {
            match self.unlock_torrents().get(&hash) {
                Some(ref t) => {
                    let stats = t.get_stats();
                    torrents.insert(hash.clone(), stats);
                }
                None => {}
            };
        }

        let mut tracker_stats = self.unlock_stats();
        tracker_stats.scrapes += 1;

        Ok(SuccessResponse::Scrape(ScrapeResponse { torrents: torrents }))
    }

    pub fn get_stats(&self) -> Result<SuccessResponse, ErrorResponse> {
        let ref stats = *self.unlock_stats();
        let resp = StatsResponse::new(stats);
        Ok(SuccessResponse::Stats(resp))
    }

    pub fn reap(&self) {
        // Clear stats
        let mut stats = self.unlock_stats();
        stats.update();
        // Delete torrents which are too old, and reap peers for the others.
        let to_del: Vec<_> = self.unlock_torrents()
            .iter()
            .filter_map(|(k, torrent)| {
                if SteadyTime::now() - torrent.last_action >
                    Duration::seconds(3600) {
                        Some(k.clone())
                    } else {
                        None
                    }
            })
        .collect();
        for torrent in to_del {
            stats.torrents -= 1;
            self.unlock_torrents().remove(&torrent);
        }

        let to_reap: Vec<_> = self.unlock_torrents()
            .iter()
            .filter_map(|(k, torrent)| {
                if SteadyTime::now() - torrent.last_action >
                    Duration::seconds(3600) {
                        None
                    } else {
                        Some(k.clone())
                    }
            })
        .collect();
        stats.peers = 0;
        for info_hash in to_reap {
            match self.unlock_torrents().get_mut(&info_hash) {
                Some(ref mut t) => {
                    t.reap();
                    stats.peers += t.get_peer_count();
                }
                None => {}
            }
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Recover if the ik failed<commit_after><|endoftext|>


<commit_before>//! The `Connect` trait, and supporting types.
//!
//! This module contains:
//!
//! - A default [`HttpConnector`](HttpConnector) that does DNS resolution and
//!   establishes connections over TCP.
//! - The [`Connect`](Connect) trait and related types to build custom connectors.
use std::error::Error as StdError;

use futures::Future;
use http::Uri;
use tokio_io::{AsyncRead, AsyncWrite};

#[cfg(feature = "runtime")] pub use self::http::HttpConnector;

/// Connect to a destination, returning an IO transport.
///
/// A connector receives a [`Destination`](Destination) describing how a
/// connection should be estabilished, and returns a `Future` of the
/// ready connection.
pub trait Connect: Send + Sync {
    /// The connected IO Stream.
    type Transport: AsyncRead + AsyncWrite + Send + 'static;
    /// An error occured when trying to connect.
    type Error: Into<Box<StdError + Send + Sync>>;
    /// A Future that will resolve to the connected Transport.
    type Future: Future<Item=(Self::Transport, Connected), Error=Self::Error> + Send;
    /// Connect to a destination.
    fn connect(&self, dst: Destination) -> Self::Future;
}

/// A set of properties to describe where and how to try to connect.
#[derive(Debug)]
pub struct Destination {
    //pub(super) alpn: Alpn,
    pub(super) uri: Uri,
}

/// Extra information about the connected transport.
///
/// This can be used to inform recipients about things like if ALPN
/// was used, or if connected to an HTTP proxy.
#[derive(Debug)]
pub struct Connected {
    //alpn: Alpn,
    pub(super) is_proxied: bool,
}

/*TODO: when HTTP1 Upgrades to H2 are added, this will be needed
#[derive(Debug)]
pub(super) enum Alpn {
    Http1,
    //H2,
    //Http1OrH2
}
*/

impl Destination {
    /// Get the protocol scheme.
    #[inline]
    pub fn scheme(&self) -> &str {
        self.uri
            .scheme_part()
            .map(|s| s.as_str())
            .unwrap_or("")
    }

    /// Get the hostname.
    #[inline]
    pub fn host(&self) -> &str {
        self.uri
            .host()
            .unwrap_or("")
    }

    /// Get the port, if specified.
    #[inline]
    pub fn port(&self) -> Option<u16> {
        self.uri.port()
    }

    /*
    /// Returns whether this connection must negotiate HTTP/2 via ALPN.
    pub fn must_h2(&self) -> bool {
        match self.alpn {
            Alpn::Http1 => false,
            Alpn::H2 => true,
        }
    }
    */
}

impl Connected {
    /// Create new `Connected` type with empty metadata.
    pub fn new() -> Connected {
        Connected {
            //alpn: Alpn::Http1,
            is_proxied: false,
        }
    }

    /// Set whether the connected transport is to an HTTP proxy.
    ///
    /// This setting will affect if HTTP/1 requests written on the transport
    /// will have the request-target in absolute-form or origin-form (such as
    /// `GET http://hyper.rs/guide HTTP/1.1` or `GET /guide HTTP/1.1`).
    ///
    /// Default is `false`.
    pub fn proxy(mut self, is_proxied: bool) -> Connected {
        self.is_proxied = is_proxied;
        self
    }

    /*
    /// Set that the connected transport negotiated HTTP/2 as it's
    /// next protocol.
    pub fn h2(mut self) -> Connected {
        self.alpn = Alpn::H2;
        self
    }
    */
}

#[cfg(feature = "runtime")]
mod http {
    use super::*;

    use std::fmt;
    use std::io;
    use std::mem;
    use std::net::SocketAddr;
    use std::sync::Arc;
    use std::time::Duration;

    use futures::{Async, Poll};
    use futures::future::{Executor, ExecuteError};
    use futures::sync::oneshot;
    use futures_cpupool::{Builder as CpuPoolBuilder};
    use http::uri::Scheme;
    use net2::TcpBuilder;
    use tokio_reactor::Handle;
    use tokio_tcp::{TcpStream, ConnectFuture};

    use super::super::dns;

    use self::http_connector::HttpConnectorBlockingTask;

    fn connect(addr: &SocketAddr, handle: &Option<Handle>) -> io::Result<ConnectFuture> {
        if let Some(ref handle) = *handle {
            let builder = match addr {
                &SocketAddr::V4(_) => TcpBuilder::new_v4()?,
                &SocketAddr::V6(_) => TcpBuilder::new_v6()?,
            };

            if cfg!(windows) {
                // Windows requires a socket be bound before calling connect
                let any: SocketAddr = match addr {
                    &SocketAddr::V4(_) => {
                        ([0, 0, 0, 0], 0).into()
                    },
                    &SocketAddr::V6(_) => {
                        ([0, 0, 0, 0, 0, 0, 0, 0], 0).into()
                    }
                };
                builder.bind(any)?;
            }

            Ok(TcpStream::connect_std(builder.to_tcp_stream()?, addr, handle))
        } else {
            Ok(TcpStream::connect(addr))
        }
    }

    /// A connector for the `http` scheme.
    ///
    /// Performs DNS resolution in a thread pool, and then connects over TCP.
    #[derive(Clone)]
    pub struct HttpConnector {
        executor: HttpConnectExecutor,
        enforce_http: bool,
        handle: Option<Handle>,
        keep_alive_timeout: Option<Duration>,
        nodelay: bool,
    }

    impl HttpConnector {
        /// Construct a new HttpConnector.
        ///
        /// Takes number of DNS worker threads.
        #[inline]
        pub fn new(threads: usize) -> HttpConnector {
            HttpConnector::new_with_handle_opt(threads, None)
        }

        /// Construct a new HttpConnector with a specific Tokio handle.
        pub fn new_with_handle(threads: usize, handle: Handle) -> HttpConnector {
            HttpConnector::new_with_handle_opt(threads, Some(handle))
        }

        fn new_with_handle_opt(threads: usize, handle: Option<Handle>) -> HttpConnector {
            let pool = CpuPoolBuilder::new()
                .name_prefix("hyper-dns")
                .pool_size(threads)
                .create();
            HttpConnector::new_with_executor(pool, handle)
        }

        /// Construct a new HttpConnector.
        ///
        /// Takes an executor to run blocking tasks on.
        pub fn new_with_executor<E: 'static>(executor: E, handle: Option<Handle>) -> HttpConnector
            where E: Executor<HttpConnectorBlockingTask> + Send + Sync
        {
            HttpConnector {
                executor: HttpConnectExecutor(Arc::new(executor)),
                enforce_http: true,
                handle,
                keep_alive_timeout: None,
                nodelay: false,
            }
        }

        /// Option to enforce all `Uri`s have the `http` scheme.
        ///
        /// Enabled by default.
        #[inline]
        pub fn enforce_http(&mut self, is_enforced: bool) {
            self.enforce_http = is_enforced;
        }

        /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration.
        ///
        /// If `None`, the option will not be set.
        ///
        /// Default is `None`.
        #[inline]
        pub fn set_keepalive(&mut self, dur: Option<Duration>) {
            self.keep_alive_timeout = dur;
        }

        /// Set that all sockets have `SO_NODELAY` set to the supplied value `nodelay`.
        ///
        /// Default is `false`.
        #[inline]
        pub fn set_nodelay(&mut self, nodelay: bool) {
            self.nodelay = nodelay;
        }
    }

    impl fmt::Debug for HttpConnector {
        #[inline]
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            f.debug_struct("HttpConnector")
                .finish()
        }
    }

    impl Connect for HttpConnector {
        type Transport = TcpStream;
        type Error = io::Error;
        type Future = HttpConnecting;

        fn connect(&self, dst: Destination) -> Self::Future {
            trace!(
                "Http::connect; scheme={}, host={}, port={:?}",
                dst.scheme(),
                dst.host(),
                dst.port(),
            );

            if self.enforce_http {
                if dst.uri.scheme_part() != Some(&Scheme::HTTP) {
                    return invalid_url(InvalidUrl::NotHttp, &self.handle);
                }
            } else if dst.uri.scheme_part().is_none() {
                return invalid_url(InvalidUrl::MissingScheme, &self.handle);
            }

            let host = match dst.uri.host() {
                Some(s) => s,
                None => return invalid_url(InvalidUrl::MissingAuthority, &self.handle),
            };
            let port = match dst.uri.port() {
                Some(port) => port,
                None => if dst.uri.scheme_part() == Some(&Scheme::HTTPS) { 443 } else { 80 },
            };

            HttpConnecting {
                state: State::Lazy(self.executor.clone(), host.into(), port),
                handle: self.handle.clone(),
                keep_alive_timeout: self.keep_alive_timeout,
                nodelay: self.nodelay,
            }
        }
    }

    #[inline]
    fn invalid_url(err: InvalidUrl, handle: &Option<Handle>) -> HttpConnecting {
        HttpConnecting {
            state: State::Error(Some(io::Error::new(io::ErrorKind::InvalidInput, err))),
            handle: handle.clone(),
            keep_alive_timeout: None,
            nodelay: false,
        }
    }

    #[derive(Debug, Clone, Copy)]
    enum InvalidUrl {
        MissingScheme,
        NotHttp,
        MissingAuthority,
    }

    impl fmt::Display for InvalidUrl {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            f.write_str(self.description())
        }
    }

    impl StdError for InvalidUrl {
        fn description(&self) -> &str {
            match *self {
                InvalidUrl::MissingScheme => "invalid URL, missing scheme",
                InvalidUrl::NotHttp => "invalid URL, scheme must be http",
                InvalidUrl::MissingAuthority => "invalid URL, missing domain",
            }
        }
    }
    /// A Future representing work to connect to a URL.
    #[must_use = "futures do nothing unless polled"]
    pub struct HttpConnecting {
        state: State,
        handle: Option<Handle>,
        keep_alive_timeout: Option<Duration>,
        nodelay: bool,
    }

    enum State {
        Lazy(HttpConnectExecutor, String, u16),
        Resolving(oneshot::SpawnHandle<dns::IpAddrs, io::Error>),
        Connecting(ConnectingTcp),
        Error(Option<io::Error>),
    }

    impl Future for HttpConnecting {
        type Item = (TcpStream, Connected);
        type Error = io::Error;

        fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
            loop {
                let state;
                match self.state {
                    State::Lazy(ref executor, ref mut host, port) => {
                        // If the host is already an IP addr (v4 or v6),
                        // skip resolving the dns and start connecting right away.
                        if let Some(addrs) = dns::IpAddrs::try_parse(host, port) {
                            state = State::Connecting(ConnectingTcp {
                                addrs: addrs,
                                current: None
                            })
                        } else {
                            let host = mem::replace(host, String::new());
                            let work = dns::Work::new(host, port);
                            state = State::Resolving(oneshot::spawn(work, executor));
                        }
                    },
                    State::Resolving(ref mut future) => {
                        match try!(future.poll()) {
                            Async::NotReady => return Ok(Async::NotReady),
                            Async::Ready(addrs) => {
                                state = State::Connecting(ConnectingTcp {
                                    addrs: addrs,
                                    current: None,
                                })
                            }
                        };
                    },
                    State::Connecting(ref mut c) => {
                        let sock = try_ready!(c.poll(&self.handle));

                        if let Some(dur) = self.keep_alive_timeout {
                            sock.set_keepalive(Some(dur))?;
                        }

                        sock.set_nodelay(self.nodelay)?;

                        return Ok(Async::Ready((sock, Connected::new())));
                    },
                    State::Error(ref mut e) => return Err(e.take().expect("polled more than once")),
                }
                self.state = state;
            }
        }
    }

    impl fmt::Debug for HttpConnecting {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            f.pad("HttpConnecting")
        }
    }

    struct ConnectingTcp {
        addrs: dns::IpAddrs,
        current: Option<ConnectFuture>,
    }

    impl ConnectingTcp {
        // not a Future, since passing a &Handle to poll
        fn poll(&mut self, handle: &Option<Handle>) -> Poll<TcpStream, io::Error> {
            let mut err = None;
            loop {
                if let Some(ref mut current) = self.current {
                    match current.poll() {
                        Ok(ok) => return Ok(ok),
                        Err(e) => {
                            trace!("connect error {:?}", e);
                            err = Some(e);
                            if let Some(addr) = self.addrs.next() {
                                debug!("connecting to {}", addr);
                                *current = connect(&addr, handle)?;
                                continue;
                            }
                        }
                    }
                } else if let Some(addr) = self.addrs.next() {
                    debug!("connecting to {}", addr);
                    self.current = Some(connect(&addr, handle)?);
                    continue;
                }

                return Err(err.take().expect("missing connect error"));
            }
        }
    }

    // Make this Future unnameable outside of this crate.
    mod http_connector {
        use super::*;
        // Blocking task to be executed on a thread pool.
        pub struct HttpConnectorBlockingTask {
            pub(super) work: oneshot::Execute<dns::Work>
        }

        impl fmt::Debug for HttpConnectorBlockingTask {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                f.pad("HttpConnectorBlockingTask")
            }
        }

        impl Future for HttpConnectorBlockingTask {
            type Item = ();
            type Error = ();

            fn poll(&mut self) -> Poll<(), ()> {
                self.work.poll()
            }
        }
    }

    #[derive(Clone)]
    struct HttpConnectExecutor(Arc<Executor<HttpConnectorBlockingTask> + Send + Sync>);

    impl Executor<oneshot::Execute<dns::Work>> for HttpConnectExecutor {
        fn execute(&self, future: oneshot::Execute<dns::Work>) -> Result<(), ExecuteError<oneshot::Execute<dns::Work>>> {
            self.0.execute(HttpConnectorBlockingTask { work: future })
                .map_err(|err| ExecuteError::new(err.kind(), err.into_future().work))
        }
    }

    #[cfg(test)]
    mod tests {
        use std::io;
        use futures::Future;
        use super::{Connect, Destination, HttpConnector};

        #[test]
        fn test_errors_missing_authority() {
            let uri = "/foo/bar?baz".parse().unwrap();
            let dst = Destination {
                uri,
            };
            let connector = HttpConnector::new(1);

            assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
        }

        #[test]
        fn test_errors_enforce_http() {
            let uri = "https://example.domain/foo/bar?baz".parse().unwrap();
            let dst = Destination {
                uri,
            };
            let connector = HttpConnector::new(1);

            assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
        }

        #[test]
        fn test_errors_missing_scheme() {
            let uri = "example.domain".parse().unwrap();
            let dst = Destination {
                uri,
            };
            let connector = HttpConnector::new(1);

            assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
        }
    }
}

<commit_msg>feat(client): support local bind for HttpConnector<commit_after>//! The `Connect` trait, and supporting types.
//!
//! This module contains:
//!
//! - A default [`HttpConnector`](HttpConnector) that does DNS resolution and
//!   establishes connections over TCP.
//! - The [`Connect`](Connect) trait and related types to build custom connectors.
use std::error::Error as StdError;

use futures::Future;
use http::Uri;
use tokio_io::{AsyncRead, AsyncWrite};

#[cfg(feature = "runtime")] pub use self::http::HttpConnector;

/// Connect to a destination, returning an IO transport.
///
/// A connector receives a [`Destination`](Destination) describing how a
/// connection should be estabilished, and returns a `Future` of the
/// ready connection.
pub trait Connect: Send + Sync {
    /// The connected IO Stream.
    type Transport: AsyncRead + AsyncWrite + Send + 'static;
    /// An error occured when trying to connect.
    type Error: Into<Box<StdError + Send + Sync>>;
    /// A Future that will resolve to the connected Transport.
    type Future: Future<Item=(Self::Transport, Connected), Error=Self::Error> + Send;
    /// Connect to a destination.
    fn connect(&self, dst: Destination) -> Self::Future;
}

/// A set of properties to describe where and how to try to connect.
#[derive(Debug)]
pub struct Destination {
    //pub(super) alpn: Alpn,
    pub(super) uri: Uri,
}

/// Extra information about the connected transport.
///
/// This can be used to inform recipients about things like if ALPN
/// was used, or if connected to an HTTP proxy.
#[derive(Debug)]
pub struct Connected {
    //alpn: Alpn,
    pub(super) is_proxied: bool,
}

/*TODO: when HTTP1 Upgrades to H2 are added, this will be needed
#[derive(Debug)]
pub(super) enum Alpn {
    Http1,
    //H2,
    //Http1OrH2
}
*/

impl Destination {
    /// Get the protocol scheme.
    #[inline]
    pub fn scheme(&self) -> &str {
        self.uri
            .scheme_part()
            .map(|s| s.as_str())
            .unwrap_or("")
    }

    /// Get the hostname.
    #[inline]
    pub fn host(&self) -> &str {
        self.uri
            .host()
            .unwrap_or("")
    }

    /// Get the port, if specified.
    #[inline]
    pub fn port(&self) -> Option<u16> {
        self.uri.port()
    }

    /*
    /// Returns whether this connection must negotiate HTTP/2 via ALPN.
    pub fn must_h2(&self) -> bool {
        match self.alpn {
            Alpn::Http1 => false,
            Alpn::H2 => true,
        }
    }
    */
}

impl Connected {
    /// Create new `Connected` type with empty metadata.
    pub fn new() -> Connected {
        Connected {
            //alpn: Alpn::Http1,
            is_proxied: false,
        }
    }

    /// Set whether the connected transport is to an HTTP proxy.
    ///
    /// This setting will affect if HTTP/1 requests written on the transport
    /// will have the request-target in absolute-form or origin-form (such as
    /// `GET http://hyper.rs/guide HTTP/1.1` or `GET /guide HTTP/1.1`).
    ///
    /// Default is `false`.
    pub fn proxy(mut self, is_proxied: bool) -> Connected {
        self.is_proxied = is_proxied;
        self
    }

    /*
    /// Set that the connected transport negotiated HTTP/2 as it's
    /// next protocol.
    pub fn h2(mut self) -> Connected {
        self.alpn = Alpn::H2;
        self
    }
    */
}

#[cfg(feature = "runtime")]
mod http {
    use super::*;

    use std::borrow::Cow;
    use std::fmt;
    use std::io;
    use std::mem;
    use std::net::{IpAddr, SocketAddr};
    use std::sync::Arc;
    use std::time::Duration;

    use futures::{Async, Poll};
    use futures::future::{Executor, ExecuteError};
    use futures::sync::oneshot;
    use futures_cpupool::{Builder as CpuPoolBuilder};
    use http::uri::Scheme;
    use net2::TcpBuilder;
    use tokio_reactor::Handle;
    use tokio_tcp::{TcpStream, ConnectFuture};

    use super::super::dns;

    use self::http_connector::HttpConnectorBlockingTask;

    fn connect(addr: &SocketAddr, local_addr: &Option<IpAddr>, handle: &Option<Handle>) -> io::Result<ConnectFuture> {
        let builder = match addr {
            &SocketAddr::V4(_) => TcpBuilder::new_v4()?,
            &SocketAddr::V6(_) => TcpBuilder::new_v6()?,
        };

        if let Some(ref local_addr) = *local_addr {
            // Caller has requested this socket be bound before calling connect
            builder.bind(SocketAddr::new(local_addr.clone(), 0))?;
        }
        else if cfg!(windows) {
            // Windows requires a socket be bound before calling connect
            let any: SocketAddr = match addr {
                &SocketAddr::V4(_) => {
                    ([0, 0, 0, 0], 0).into()
                },
                &SocketAddr::V6(_) => {
                    ([0, 0, 0, 0, 0, 0, 0, 0], 0).into()
                }
            };
            builder.bind(any)?;
        }

        let handle = match *handle {
            Some(ref handle) => Cow::Borrowed(handle),
            None => Cow::Owned(Handle::current()),
        };

        Ok(TcpStream::connect_std(builder.to_tcp_stream()?, addr, &handle))
    }

    /// A connector for the `http` scheme.
    ///
    /// Performs DNS resolution in a thread pool, and then connects over TCP.
    #[derive(Clone)]
    pub struct HttpConnector {
        executor: HttpConnectExecutor,
        enforce_http: bool,
        handle: Option<Handle>,
        keep_alive_timeout: Option<Duration>,
        nodelay: bool,
        local_address: Option<IpAddr>,
    }

    impl HttpConnector {
        /// Construct a new HttpConnector.
        ///
        /// Takes number of DNS worker threads.
        #[inline]
        pub fn new(threads: usize) -> HttpConnector {
            HttpConnector::new_with_handle_opt(threads, None)
        }

        /// Construct a new HttpConnector with a specific Tokio handle.
        pub fn new_with_handle(threads: usize, handle: Handle) -> HttpConnector {
            HttpConnector::new_with_handle_opt(threads, Some(handle))
        }

        fn new_with_handle_opt(threads: usize, handle: Option<Handle>) -> HttpConnector {
            let pool = CpuPoolBuilder::new()
                .name_prefix("hyper-dns")
                .pool_size(threads)
                .create();
            HttpConnector::new_with_executor(pool, handle)
        }

        /// Construct a new HttpConnector.
        ///
        /// Takes an executor to run blocking tasks on.
        pub fn new_with_executor<E: 'static>(executor: E, handle: Option<Handle>) -> HttpConnector
            where E: Executor<HttpConnectorBlockingTask> + Send + Sync
        {
            HttpConnector {
                executor: HttpConnectExecutor(Arc::new(executor)),
                enforce_http: true,
                handle,
                keep_alive_timeout: None,
                nodelay: false,
                local_address: None,
            }
        }

        /// Option to enforce all `Uri`s have the `http` scheme.
        ///
        /// Enabled by default.
        #[inline]
        pub fn enforce_http(&mut self, is_enforced: bool) {
            self.enforce_http = is_enforced;
        }

        /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration.
        ///
        /// If `None`, the option will not be set.
        ///
        /// Default is `None`.
        #[inline]
        pub fn set_keepalive(&mut self, dur: Option<Duration>) {
            self.keep_alive_timeout = dur;
        }

        /// Set that all sockets have `SO_NODELAY` set to the supplied value `nodelay`.
        ///
        /// Default is `false`.
        #[inline]
        pub fn set_nodelay(&mut self, nodelay: bool) {
            self.nodelay = nodelay;
        }

        /// Set that all sockets are bound to the configured address before connection.
        ///
        /// If `None`, the sockets will not be bound.
        ///
        /// Default is `None`.
        #[inline]
        pub fn set_local_address(&mut self, addr: Option<IpAddr>) {
            self.local_address = addr;
        }
    }

    impl fmt::Debug for HttpConnector {
        #[inline]
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            f.debug_struct("HttpConnector")
                .finish()
        }
    }

    impl Connect for HttpConnector {
        type Transport = TcpStream;
        type Error = io::Error;
        type Future = HttpConnecting;

        fn connect(&self, dst: Destination) -> Self::Future {
            trace!(
                "Http::connect; scheme={}, host={}, port={:?}",
                dst.scheme(),
                dst.host(),
                dst.port(),
            );

            if self.enforce_http {
                if dst.uri.scheme_part() != Some(&Scheme::HTTP) {
                    return invalid_url(InvalidUrl::NotHttp, &self.handle);
                }
            } else if dst.uri.scheme_part().is_none() {
                return invalid_url(InvalidUrl::MissingScheme, &self.handle);
            }

            let host = match dst.uri.host() {
                Some(s) => s,
                None => return invalid_url(InvalidUrl::MissingAuthority, &self.handle),
            };
            let port = match dst.uri.port() {
                Some(port) => port,
                None => if dst.uri.scheme_part() == Some(&Scheme::HTTPS) { 443 } else { 80 },
            };

            HttpConnecting {
                state: State::Lazy(self.executor.clone(), host.into(), port, self.local_address),
                handle: self.handle.clone(),
                keep_alive_timeout: self.keep_alive_timeout,
                nodelay: self.nodelay,
            }
        }
    }

    #[inline]
    fn invalid_url(err: InvalidUrl, handle: &Option<Handle>) -> HttpConnecting {
        HttpConnecting {
            state: State::Error(Some(io::Error::new(io::ErrorKind::InvalidInput, err))),
            handle: handle.clone(),
            keep_alive_timeout: None,
            nodelay: false,
        }
    }

    #[derive(Debug, Clone, Copy)]
    enum InvalidUrl {
        MissingScheme,
        NotHttp,
        MissingAuthority,
    }

    impl fmt::Display for InvalidUrl {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            f.write_str(self.description())
        }
    }

    impl StdError for InvalidUrl {
        fn description(&self) -> &str {
            match *self {
                InvalidUrl::MissingScheme => "invalid URL, missing scheme",
                InvalidUrl::NotHttp => "invalid URL, scheme must be http",
                InvalidUrl::MissingAuthority => "invalid URL, missing domain",
            }
        }
    }
    /// A Future representing work to connect to a URL.
    #[must_use = "futures do nothing unless polled"]
    pub struct HttpConnecting {
        state: State,
        handle: Option<Handle>,
        keep_alive_timeout: Option<Duration>,
        nodelay: bool,
    }

    enum State {
        Lazy(HttpConnectExecutor, String, u16, Option<IpAddr>),
        Resolving(oneshot::SpawnHandle<dns::IpAddrs, io::Error>, Option<IpAddr>),
        Connecting(ConnectingTcp),
        Error(Option<io::Error>),
    }

    impl Future for HttpConnecting {
        type Item = (TcpStream, Connected);
        type Error = io::Error;

        fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
            loop {
                let state;
                match self.state {
                    State::Lazy(ref executor, ref mut host, port, local_addr) => {
                        // If the host is already an IP addr (v4 or v6),
                        // skip resolving the dns and start connecting right away.
                        if let Some(addrs) = dns::IpAddrs::try_parse(host, port) {
                            state = State::Connecting(ConnectingTcp {
                                addrs: addrs,
                                local_addr: local_addr,
                                current: None
                            })
                        } else {
                            let host = mem::replace(host, String::new());
                            let work = dns::Work::new(host, port);
                            state = State::Resolving(oneshot::spawn(work, executor), local_addr);
                        }
                    },
                    State::Resolving(ref mut future, local_addr) => {
                        match try!(future.poll()) {
                            Async::NotReady => return Ok(Async::NotReady),
                            Async::Ready(addrs) => {
                                state = State::Connecting(ConnectingTcp {
                                    addrs: addrs,
                                    local_addr: local_addr,
                                    current: None,
                                })
                            }
                        };
                    },
                    State::Connecting(ref mut c) => {
                        let sock = try_ready!(c.poll(&self.handle));

                        if let Some(dur) = self.keep_alive_timeout {
                            sock.set_keepalive(Some(dur))?;
                        }

                        sock.set_nodelay(self.nodelay)?;

                        return Ok(Async::Ready((sock, Connected::new())));
                    },
                    State::Error(ref mut e) => return Err(e.take().expect("polled more than once")),
                }
                self.state = state;
            }
        }
    }

    impl fmt::Debug for HttpConnecting {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            f.pad("HttpConnecting")
        }
    }

    struct ConnectingTcp {
        addrs: dns::IpAddrs,
        local_addr: Option<IpAddr>,
        current: Option<ConnectFuture>,
    }

    impl ConnectingTcp {
        // not a Future, since passing a &Handle to poll
        fn poll(&mut self, handle: &Option<Handle>) -> Poll<TcpStream, io::Error> {
            let mut err = None;
            loop {
                if let Some(ref mut current) = self.current {
                    match current.poll() {
                        Ok(ok) => return Ok(ok),
                        Err(e) => {
                            trace!("connect error {:?}", e);
                            err = Some(e);
                            if let Some(addr) = self.addrs.next() {
                                debug!("connecting to {}", addr);
                                *current = connect(&addr, &self.local_addr, handle)?;
                                continue;
                            }
                        }
                    }
                } else if let Some(addr) = self.addrs.next() {
                    debug!("connecting to {}", addr);
                    self.current = Some(connect(&addr, &self.local_addr, handle)?);
                    continue;
                }

                return Err(err.take().expect("missing connect error"));
            }
        }
    }

    // Make this Future unnameable outside of this crate.
    mod http_connector {
        use super::*;
        // Blocking task to be executed on a thread pool.
        pub struct HttpConnectorBlockingTask {
            pub(super) work: oneshot::Execute<dns::Work>
        }

        impl fmt::Debug for HttpConnectorBlockingTask {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                f.pad("HttpConnectorBlockingTask")
            }
        }

        impl Future for HttpConnectorBlockingTask {
            type Item = ();
            type Error = ();

            fn poll(&mut self) -> Poll<(), ()> {
                self.work.poll()
            }
        }
    }

    #[derive(Clone)]
    struct HttpConnectExecutor(Arc<Executor<HttpConnectorBlockingTask> + Send + Sync>);

    impl Executor<oneshot::Execute<dns::Work>> for HttpConnectExecutor {
        fn execute(&self, future: oneshot::Execute<dns::Work>) -> Result<(), ExecuteError<oneshot::Execute<dns::Work>>> {
            self.0.execute(HttpConnectorBlockingTask { work: future })
                .map_err(|err| ExecuteError::new(err.kind(), err.into_future().work))
        }
    }

    #[cfg(test)]
    mod tests {
        use std::io;
        use futures::Future;
        use super::{Connect, Destination, HttpConnector};

        #[test]
        fn test_errors_missing_authority() {
            let uri = "/foo/bar?baz".parse().unwrap();
            let dst = Destination {
                uri,
            };
            let connector = HttpConnector::new(1);

            assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
        }

        #[test]
        fn test_errors_enforce_http() {
            let uri = "https://example.domain/foo/bar?baz".parse().unwrap();
            let dst = Destination {
                uri,
            };
            let connector = HttpConnector::new(1);

            assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
        }

        #[test]
        fn test_errors_missing_scheme() {
            let uri = "example.domain".parse().unwrap();
            let dst = Destination {
                uri,
            };
            let connector = HttpConnector::new(1);

            assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
        }
    }
}

<|endoftext|>


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

use eutil::slice_to_str;
use libc::{c_int};
use std::collections::TreeMap;
use std::mem;
use std::string::String;
use string::{cef_string_userfree_utf8_alloc,cef_string_userfree_utf8_free,cef_string_utf8_set};
use types::{cef_string_multimap_t,cef_string_t};

//cef_string_multimap

#[no_mangle]
pub extern "C" fn cef_string_multimap_alloc() -> *mut cef_string_multimap_t {
    unsafe {
         let smm: Box<TreeMap<String, Vec<*mut cef_string_t>>> = box TreeMap::new();
         mem::transmute(smm)
    }
}
<commit_msg>embedding: cef_string_multimap_size()<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use eutil::slice_to_str;
use libc::{c_int};
use std::collections::TreeMap;
use std::mem;
use std::string::String;
use string::{cef_string_userfree_utf8_alloc,cef_string_userfree_utf8_free,cef_string_utf8_set};
use types::{cef_string_multimap_t,cef_string_t};

fn string_multimap_to_treemap(smm: *mut cef_string_multimap_t) -> *mut TreeMap<String, Vec<*mut cef_string_t>> {
    smm as *mut TreeMap<String, Vec<*mut cef_string_t>>
}

//cef_string_multimap

#[no_mangle]
pub extern "C" fn cef_string_multimap_alloc() -> *mut cef_string_multimap_t {
    unsafe {
         let smm: Box<TreeMap<String, Vec<*mut cef_string_t>>> = box TreeMap::new();
         mem::transmute(smm)
    }
}

#[no_mangle]
pub extern "C" fn cef_string_multimap_size(smm: *mut cef_string_multimap_t) -> c_int {
    unsafe {
        if smm.is_null() { return 0; }
        let mut c: c_int = 0;
        let v = string_multimap_to_treemap(smm);
        for (_, val) in (*v).iter() {
            c = c + (*val).len() as c_int;
        }
        c
    }
}
<|endoftext|>


<commit_before><commit_msg>Cargo fmt<commit_after><|endoftext|>