<commit_before>//! socketCAN support.
//!
//! The Linux kernel supports using CAN-devices through a network-like API
//! (see https://www.kernel.org/doc/Documentation/networking/can.txt). This
//! crate allows easy access to this functionality without having to wrestle
//! libc calls.

extern crate itertools;
extern crate libc;
extern crate nix;

use libc::{c_int, c_short, c_void, c_uint, socket, SOCK_RAW, close, bind,
           sockaddr, read, write, setsockopt, SOL_SOCKET, SO_RCVTIMEO,
           timeval, EINPROGRESS};
use itertools::Itertools;
use std::{error, fmt, io, time};
use std::mem::size_of;
use nix::net::if_::if_nametoindex;

/// Check an error return value for timeouts.
///
/// Due to the fact that timeouts are reported as errors, calling `read_frame`
/// on a socket with a timeout that does not receive a frame in time will
/// result in an error being returned. This trait adds a `should_retry` method
/// to `Error` and `Result` to check for this condition.
trait ShouldRetry {
    /// Check for timeout
    ///
    /// If `true`, the error is probably due to a timeout.
    fn should_retry(&self) -> bool;
}

impl ShouldRetry for io::Error {
    fn should_retry(&self) -> bool {
        match self.kind() {
            // EAGAIN, EINPROGRESS and EWOULDBLOCK are the three possible codes
            // returned when a timeout occurs. the stdlib already maps EAGAIN
            // and EWOULDBLOCK os WouldBlock
            io::ErrorKind::WouldBlock => true,
            // however, EINPROGRESS is also valid
            io::ErrorKind::Other => {
                if let Some(i) = self.raw_os_error() {
                    i == EINPROGRESS
                } else {
                    false
                }
            },
            _ => false,
        }
    }
}

impl<E: fmt::Debug> ShouldRetry for io::Result<E> {
    fn should_retry(&self) -> bool {
        if let &Err(ref e) = self {
            e.should_retry()
        } else {
            false
        }
    }
}

#[cfg(test)]
mod tests;

// constants stolen from C headers
const AF_CAN: c_int = 29;
const PF_CAN: c_int = 29;
const CAN_RAW: c_int = 1;

/// if set, indicate 29 bit extended format
pub const EFF_FLAG: u32 = 0x80000000;

/// remote transmission request flag
pub const RTR_FLAG: u32 = 0x40000000;

/// error flag
pub const ERR_FLAG: u32 = 0x20000000;

/// valid bits in standard frame id
pub const SFF_MASK: u32 = 0x000007ff;

/// valid bits in extended frame id
pub const EFF_MASK: u32 = 0x1fffffff;

/// valid bits in error frame
pub const ERR_MASK: u32 = 0x1fffffff;

fn c_timeval_new(t: time::Duration) -> timeval {
    timeval {
        tv_sec: t.as_secs() as i64,
        tv_usec: (t.subsec_nanos() / 1000) as i64,
    }
}

#[derive(Debug)]
#[repr(C)]
struct CANAddr {
    _af_can: c_short,
    if_index: c_int,  // address familiy,
    rx_id: u32,
    tx_id: u32,
}

#[derive(Debug)]
/// Errors opening socket
pub enum CANSocketOpenError {
    /// Device could not be found
    LookupError(nix::Error),

    /// System error while trying to look up device name
    IOError(io::Error),
}

impl fmt::Display for CANSocketOpenError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CANSocketOpenError::LookupError(ref e) =>
                write!(f, "CAN Device not found: {}", e),
            CANSocketOpenError::IOError(ref e) =>
                write!(f, "IO: {}", e),
        }
    }
}

impl error::Error for CANSocketOpenError {
    fn description(&self) -> &str {
        match *self {
            CANSocketOpenError::LookupError(_)
                => "can device not found",
            CANSocketOpenError::IOError(ref e)
                => e.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            CANSocketOpenError::LookupError(ref e) => Some(e),
            CANSocketOpenError::IOError(ref e) => Some(e),
        }
    }
}

#[derive(Debug, Copy, Clone)]
/// Error that occurs when creating CAN packets
pub enum ConstructionError {
    /// CAN ID was outside the range of valid IDs
    IDTooLarge,
    /// More than 8 Bytes of payload data were passed in
    TooMuchData,
}

impl fmt::Display for ConstructionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConstructionError::IDTooLarge
                => write!(f, "CAN ID too large"),
            ConstructionError::TooMuchData
                => write!(f, "Payload is larger than CAN maximum of 8 bytes")
        }
    }
}

impl error::Error for ConstructionError {
    fn description(&self) -> &str {
        match *self {
            ConstructionError::IDTooLarge => "can id too large",
            ConstructionError::TooMuchData => "too much data",
        }
    }
}

impl From<nix::Error> for CANSocketOpenError {
    fn from(e: nix::Error) -> CANSocketOpenError {
        CANSocketOpenError::LookupError(e)
    }
}

impl From<io::Error> for CANSocketOpenError {
    fn from(e: io::Error) -> CANSocketOpenError {
        CANSocketOpenError::IOError(e)
    }
}

/// A socket for a CAN device.
///
/// Will be closed upon deallocation. To close manually, use std::drop::Drop.
/// Internally this is just a wrapped file-descriptor.
pub struct CANSocket {
    fd: c_int,
}

impl CANSocket {
    /// Open a named CAN device.
    ///
    /// Usually the more common case, opens a socket can device by name, such
    /// as "vcan0" or "socan0".
    pub fn open(ifname: &str) -> Result<CANSocket, CANSocketOpenError> {
        let if_index = try!(if_nametoindex(ifname));
        CANSocket::open_if(if_index)
    }

    /// Open CAN device by interface number.
    ///
    /// Opens a CAN device by kernel interface number.
    pub fn open_if(if_index: c_uint) -> Result<CANSocket, CANSocketOpenError> {
        let addr = CANAddr{
            _af_can: AF_CAN as c_short,
            if_index: if_index as c_int,
            rx_id: 0, // ?
            tx_id: 0, // ?
        };

        // open socket
        let sock_fd;
        unsafe {
            sock_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
        }

        if sock_fd == -1 {
            return Err(CANSocketOpenError::from(io::Error::last_os_error()));
        }

        // bind it
        let bind_rv;
        unsafe {
            let sockaddr_ptr = &addr as *const CANAddr;
            bind_rv = bind(sock_fd, sockaddr_ptr as *const sockaddr,
                size_of::<CANAddr> () as u32);
        }

        if bind_rv == -1 {
            let e = io::Error::last_os_error();
            unsafe {
                close(sock_fd);
            }
            return Err(CANSocketOpenError::from(e))
        }

        Ok(CANSocket{fd: sock_fd})
    }

    fn close(&mut self) -> io::Result<()> {
        unsafe {
            let rv = close(self.fd);
            if rv != -1 {
                return Err(io::Error::last_os_error());
            }
        }
        Ok(())
    }

    /// Sets the read timeout on the socket
    ///
    /// For convenience, the result value can be checked using
    /// `ShouldRetry::should_retry` when a timeout is set.
    pub fn set_read_timeout(&self, duration: time::Duration) -> io::Result<()> {
        let rv = unsafe {
            let tv = c_timeval_new(duration);
            let tv_ptr: *const timeval = &tv as *const timeval;
            setsockopt(self.fd, SOL_SOCKET, SO_RCVTIMEO,
                       tv_ptr as *const c_void, size_of::<timeval>() as u32)
        };

        if rv != 0 {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }

    /// Blocking read a single can frame.
    pub fn read_frame(&self) -> io::Result<CANFrame> {
        let mut frame = CANFrame{
            _id: 0,
            _data_len: 0,
            _pad: 0,
            _res0: 0,
            _res1: 0,
            _data: [0; 8],
        };

        let read_rv = unsafe {
            let frame_ptr = &mut frame as *mut CANFrame;
            read(self.fd, frame_ptr as *mut c_void, size_of::<CANFrame>())
        };

        if read_rv as usize != size_of::<CANFrame>() {
            return Err(io::Error::last_os_error());
        }

        Ok(frame)
    }

    /// Blocking write a single can frame.
    pub fn write_frame(&self, frame: &CANFrame) -> io::Result<()> {
        // not a mutable reference needed (see std::net::UdpSocket) for
        // a comparison
        // debug!("Sending: {:?}", frame);

        let write_rv = unsafe {
            let frame_ptr = frame as *const CANFrame;
            write(self.fd, frame_ptr as *const c_void, size_of::<CANFrame>())
        };

        if write_rv as usize != size_of::<CANFrame>() {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }
}

impl Drop for CANSocket {
    fn drop(&mut self) {
        self.close().ok();  // ignore result
    }
}

/// CANFrame
///
/// Uses the same memory layout as the underlying kernel struct for performance
/// reasons.
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct CANFrame {
    /// 32 bit CAN_ID + EFF/RTR/ERR flags
    _id: u32,

    /// data length. Bytes beyond are not valid
    _data_len: u8,

    /// padding
    _pad: u8,

    /// reserved
    _res0: u8,

    /// reserved
    _res1: u8,

    /// buffer for data
    _data: [u8; 8],
}

impl CANFrame {
    pub fn new(id: u32, data: &[u8], rtr: bool, err: bool) -> Result<CANFrame,
    ConstructionError> {
        let mut _id = id;

        if data.len() > 8 {
            return Err(ConstructionError::TooMuchData)
        }

        if id > EFF_MASK {
            return Err(ConstructionError::IDTooLarge)
        }

        // set EFF_FLAG on large message
        if id > SFF_MASK {
            _id |= EFF_FLAG;
        }

        if rtr {
            _id |= RTR_FLAG;
        }

        if err {
            _id |= ERR_FLAG;
        }

        let mut full_data = [0; 8];

        // not cool =/
        for (n, c) in data.iter().enumerate() {
            full_data[n] = *c;
        }

        Ok(CANFrame{
            _id: _id,
            _data_len: data.len() as u8,
            _pad: 0,
            _res0: 0,
            _res1: 0,
            _data: full_data,
        })
    }

    /// Return the actual CAN ID (without EFF/RTR/ERR flags)
    pub fn id(&self) -> u32 {
        if self.is_extended() {
            self._id & EFF_MASK
        } else {
            self._id & SFF_MASK
        }
    }

    /// Return the error message
    pub fn err(&self) -> u32 {
        return self._id & ERR_MASK
    }

    /// Check if frame uses 29 bit extended frame format
    pub fn is_extended(&self) -> bool {
        self._id & EFF_FLAG != 0
    }

    /// Check if frame is an error message
    pub fn is_error(&self) -> bool {
        self._id & ERR_FLAG != 0
    }

    /// Check if frame is a remote transmission request
    pub fn is_rtr(&self) -> bool {
        self._id & RTR_FLAG != 0
    }

    /// A slice into the actual data. Slice will always be <= 8 bytes in length
    pub fn data(&self) -> &[u8] {
        &self._data[..(self._data_len as usize)]
    }
}

impl fmt::UpperHex for CANFrame {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(write!(f, "{:X}#", self.id()));

        let mut parts = self.data().iter().map(|v| format!("{:02X}", v));

        let sep = if f.alternate() { " " } else { " " };
        write!(f, "{}", parts.join(sep))
    }
}
<commit_msg>Add support for send timeout.<commit_after>//! socketCAN support.
//!
//! The Linux kernel supports using CAN-devices through a network-like API
//! (see https://www.kernel.org/doc/Documentation/networking/can.txt). This
//! crate allows easy access to this functionality without having to wrestle
//! libc calls.

extern crate itertools;
extern crate libc;
extern crate nix;

use libc::{c_int, c_short, c_void, c_uint, socket, SOCK_RAW, close, bind,
           sockaddr, read, write, setsockopt, SOL_SOCKET, SO_RCVTIMEO,
           timeval, EINPROGRESS, SO_SNDTIMEO};
use itertools::Itertools;
use std::{error, fmt, io, time};
use std::mem::size_of;
use nix::net::if_::if_nametoindex;

/// Check an error return value for timeouts.
///
/// Due to the fact that timeouts are reported as errors, calling `read_frame`
/// on a socket with a timeout that does not receive a frame in time will
/// result in an error being returned. This trait adds a `should_retry` method
/// to `Error` and `Result` to check for this condition.
trait ShouldRetry {
    /// Check for timeout
    ///
    /// If `true`, the error is probably due to a timeout.
    fn should_retry(&self) -> bool;
}

impl ShouldRetry for io::Error {
    fn should_retry(&self) -> bool {
        match self.kind() {
            // EAGAIN, EINPROGRESS and EWOULDBLOCK are the three possible codes
            // returned when a timeout occurs. the stdlib already maps EAGAIN
            // and EWOULDBLOCK os WouldBlock
            io::ErrorKind::WouldBlock => true,
            // however, EINPROGRESS is also valid
            io::ErrorKind::Other => {
                if let Some(i) = self.raw_os_error() {
                    i == EINPROGRESS
                } else {
                    false
                }
            },
            _ => false,
        }
    }
}

impl<E: fmt::Debug> ShouldRetry for io::Result<E> {
    fn should_retry(&self) -> bool {
        if let &Err(ref e) = self {
            e.should_retry()
        } else {
            false
        }
    }
}

#[cfg(test)]
mod tests;

// constants stolen from C headers
const AF_CAN: c_int = 29;
const PF_CAN: c_int = 29;
const CAN_RAW: c_int = 1;

/// if set, indicate 29 bit extended format
pub const EFF_FLAG: u32 = 0x80000000;

/// remote transmission request flag
pub const RTR_FLAG: u32 = 0x40000000;

/// error flag
pub const ERR_FLAG: u32 = 0x20000000;

/// valid bits in standard frame id
pub const SFF_MASK: u32 = 0x000007ff;

/// valid bits in extended frame id
pub const EFF_MASK: u32 = 0x1fffffff;

/// valid bits in error frame
pub const ERR_MASK: u32 = 0x1fffffff;

fn c_timeval_new(t: time::Duration) -> timeval {
    timeval {
        tv_sec: t.as_secs() as i64,
        tv_usec: (t.subsec_nanos() / 1000) as i64,
    }
}

#[derive(Debug)]
#[repr(C)]
struct CANAddr {
    _af_can: c_short,
    if_index: c_int,  // address familiy,
    rx_id: u32,
    tx_id: u32,
}

#[derive(Debug)]
/// Errors opening socket
pub enum CANSocketOpenError {
    /// Device could not be found
    LookupError(nix::Error),

    /// System error while trying to look up device name
    IOError(io::Error),
}

impl fmt::Display for CANSocketOpenError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CANSocketOpenError::LookupError(ref e) =>
                write!(f, "CAN Device not found: {}", e),
            CANSocketOpenError::IOError(ref e) =>
                write!(f, "IO: {}", e),
        }
    }
}

impl error::Error for CANSocketOpenError {
    fn description(&self) -> &str {
        match *self {
            CANSocketOpenError::LookupError(_)
                => "can device not found",
            CANSocketOpenError::IOError(ref e)
                => e.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            CANSocketOpenError::LookupError(ref e) => Some(e),
            CANSocketOpenError::IOError(ref e) => Some(e),
        }
    }
}

#[derive(Debug, Copy, Clone)]
/// Error that occurs when creating CAN packets
pub enum ConstructionError {
    /// CAN ID was outside the range of valid IDs
    IDTooLarge,
    /// More than 8 Bytes of payload data were passed in
    TooMuchData,
}

impl fmt::Display for ConstructionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConstructionError::IDTooLarge
                => write!(f, "CAN ID too large"),
            ConstructionError::TooMuchData
                => write!(f, "Payload is larger than CAN maximum of 8 bytes")
        }
    }
}

impl error::Error for ConstructionError {
    fn description(&self) -> &str {
        match *self {
            ConstructionError::IDTooLarge => "can id too large",
            ConstructionError::TooMuchData => "too much data",
        }
    }
}

impl From<nix::Error> for CANSocketOpenError {
    fn from(e: nix::Error) -> CANSocketOpenError {
        CANSocketOpenError::LookupError(e)
    }
}

impl From<io::Error> for CANSocketOpenError {
    fn from(e: io::Error) -> CANSocketOpenError {
        CANSocketOpenError::IOError(e)
    }
}

/// A socket for a CAN device.
///
/// Will be closed upon deallocation. To close manually, use std::drop::Drop.
/// Internally this is just a wrapped file-descriptor.
pub struct CANSocket {
    fd: c_int,
}

impl CANSocket {
    /// Open a named CAN device.
    ///
    /// Usually the more common case, opens a socket can device by name, such
    /// as "vcan0" or "socan0".
    pub fn open(ifname: &str) -> Result<CANSocket, CANSocketOpenError> {
        let if_index = try!(if_nametoindex(ifname));
        CANSocket::open_if(if_index)
    }

    /// Open CAN device by interface number.
    ///
    /// Opens a CAN device by kernel interface number.
    pub fn open_if(if_index: c_uint) -> Result<CANSocket, CANSocketOpenError> {
        let addr = CANAddr{
            _af_can: AF_CAN as c_short,
            if_index: if_index as c_int,
            rx_id: 0, // ?
            tx_id: 0, // ?
        };

        // open socket
        let sock_fd;
        unsafe {
            sock_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
        }

        if sock_fd == -1 {
            return Err(CANSocketOpenError::from(io::Error::last_os_error()));
        }

        // bind it
        let bind_rv;
        unsafe {
            let sockaddr_ptr = &addr as *const CANAddr;
            bind_rv = bind(sock_fd, sockaddr_ptr as *const sockaddr,
                size_of::<CANAddr> () as u32);
        }

        if bind_rv == -1 {
            let e = io::Error::last_os_error();
            unsafe {
                close(sock_fd);
            }
            return Err(CANSocketOpenError::from(e))
        }

        Ok(CANSocket{fd: sock_fd})
    }

    fn close(&mut self) -> io::Result<()> {
        unsafe {
            let rv = close(self.fd);
            if rv != -1 {
                return Err(io::Error::last_os_error());
            }
        }
        Ok(())
    }

    /// Sets the read timeout on the socket
    ///
    /// For convenience, the result value can be checked using
    /// `ShouldRetry::should_retry` when a timeout is set.
    pub fn set_read_timeout(&self, duration: time::Duration) -> io::Result<()> {
        let rv = unsafe {
            let tv = c_timeval_new(duration);
            let tv_ptr: *const timeval = &tv as *const timeval;
            setsockopt(self.fd, SOL_SOCKET, SO_RCVTIMEO,
                       tv_ptr as *const c_void, size_of::<timeval>() as u32)
        };

        if rv != 0 {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }

    /// Sets the write timeout on the socket
    pub fn set_write_timeout(&self, duration: time::Duration) -> io::Result<()> {
        let rv = unsafe {
            let tv = c_timeval_new(duration);
            let tv_ptr: *const timeval = &tv as *const timeval;
            setsockopt(self.fd, SOL_SOCKET, SO_SNDTIMEO,
                       tv_ptr as *const c_void, size_of::<timeval>() as u32)
        };

        if rv != 0 {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }

    /// Blocking read a single can frame.
    pub fn read_frame(&self) -> io::Result<CANFrame> {
        let mut frame = CANFrame{
            _id: 0,
            _data_len: 0,
            _pad: 0,
            _res0: 0,
            _res1: 0,
            _data: [0; 8],
        };

        let read_rv = unsafe {
            let frame_ptr = &mut frame as *mut CANFrame;
            read(self.fd, frame_ptr as *mut c_void, size_of::<CANFrame>())
        };

        if read_rv as usize != size_of::<CANFrame>() {
            return Err(io::Error::last_os_error());
        }

        Ok(frame)
    }

    /// Blocking write a single can frame.
    pub fn write_frame(&self, frame: &CANFrame) -> io::Result<()> {
        // not a mutable reference needed (see std::net::UdpSocket) for
        // a comparison
        // debug!("Sending: {:?}", frame);

        let write_rv = unsafe {
            let frame_ptr = frame as *const CANFrame;
            write(self.fd, frame_ptr as *const c_void, size_of::<CANFrame>())
        };

        if write_rv as usize != size_of::<CANFrame>() {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }
}

impl Drop for CANSocket {
    fn drop(&mut self) {
        self.close().ok();  // ignore result
    }
}

/// CANFrame
///
/// Uses the same memory layout as the underlying kernel struct for performance
/// reasons.
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct CANFrame {
    /// 32 bit CAN_ID + EFF/RTR/ERR flags
    _id: u32,

    /// data length. Bytes beyond are not valid
    _data_len: u8,

    /// padding
    _pad: u8,

    /// reserved
    _res0: u8,

    /// reserved
    _res1: u8,

    /// buffer for data
    _data: [u8; 8],
}

impl CANFrame {
    pub fn new(id: u32, data: &[u8], rtr: bool, err: bool) -> Result<CANFrame,
    ConstructionError> {
        let mut _id = id;

        if data.len() > 8 {
            return Err(ConstructionError::TooMuchData)
        }

        if id > EFF_MASK {
            return Err(ConstructionError::IDTooLarge)
        }

        // set EFF_FLAG on large message
        if id > SFF_MASK {
            _id |= EFF_FLAG;
        }

        if rtr {
            _id |= RTR_FLAG;
        }

        if err {
            _id |= ERR_FLAG;
        }

        let mut full_data = [0; 8];

        // not cool =/
        for (n, c) in data.iter().enumerate() {
            full_data[n] = *c;
        }

        Ok(CANFrame{
            _id: _id,
            _data_len: data.len() as u8,
            _pad: 0,
            _res0: 0,
            _res1: 0,
            _data: full_data,
        })
    }

    /// Return the actual CAN ID (without EFF/RTR/ERR flags)
    pub fn id(&self) -> u32 {
        if self.is_extended() {
            self._id & EFF_MASK
        } else {
            self._id & SFF_MASK
        }
    }

    /// Return the error message
    pub fn err(&self) -> u32 {
        return self._id & ERR_MASK
    }

    /// Check if frame uses 29 bit extended frame format
    pub fn is_extended(&self) -> bool {
        self._id & EFF_FLAG != 0
    }

    /// Check if frame is an error message
    pub fn is_error(&self) -> bool {
        self._id & ERR_FLAG != 0
    }

    /// Check if frame is a remote transmission request
    pub fn is_rtr(&self) -> bool {
        self._id & RTR_FLAG != 0
    }

    /// A slice into the actual data. Slice will always be <= 8 bytes in length
    pub fn data(&self) -> &[u8] {
        &self._data[..(self._data_len as usize)]
    }
}

impl fmt::UpperHex for CANFrame {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(write!(f, "{:X}#", self.id()));

        let mut parts = self.data().iter().map(|v| format!("{:02X}", v));

        let sep = if f.alternate() { " " } else { " " };
        write!(f, "{}", parts.join(sep))
    }
}
<|endoftext|>


<commit_before>#![feature(no_std, core, core_prelude, asm, raw)]
#![no_std]

#![crate_name = "raw_cpuid"]
#![crate_type = "lib"]

#[macro_use]
extern crate core;

#[macro_use]
extern crate bitflags;

#[cfg(test)]
#[macro_use]
extern crate std;

#[cfg(test)]
#[prelude_import]
use std::prelude::v1::*;

use core::prelude::*;
use core::iter;
use core::raw;
use core::str;
use core::mem::{transmute};
use core::fmt;
use core::slice;

#[cfg(not(test))]
mod std {
    pub use core::ops;
    pub use core::option;
}

const MAX_ENTRIES: usize = 32;

#[macro_export]
macro_rules! cpuid {
    ($eax:expr)
        => ( $crate::cpuid1($eax as u32) );

    ($eax:expr, $ecx:expr)
        => ( $crate::cpuid2($eax as u32, $ecx as u32) );

}

fn cpuid2(eax: u32, ecx: u32) -> CpuIdResult {
    let mut res = CpuIdResult{eax: 0, ebx: 0, ecx: 0, edx: 0};

    unsafe {
        asm!("movl $0, %eax" : : "r" (eax) : "eax");
        asm!("movl $0, %ecx" : : "r" (ecx) : "ecx");
        asm!("cpuid" : "={eax}"(res.eax) "={ebx}"(res.ebx)
                       "={ecx}"(res.ecx) "={edx}"(res.edx)
                     :: "eax", "ebx", "ecx", "edx");
    }

    res
}

fn cpuid1(eax: u32) -> CpuIdResult {
    let mut res = CpuIdResult{eax: 0, ebx: 0, ecx: 0, edx: 0};

    unsafe {
        asm!("movl $0, %eax" : : "r" (eax) : "eax");
        asm!("cpuid" : "={eax}"(res.eax) "={ebx}"(res.ebx)
                       "={ecx}"(res.ecx) "={edx}"(res.edx)
                     :: "eax", "ebx", "ecx", "edx");
    }

    res
}

enum CpuIdLeaf {
    VendorInformation,
    FeatureInformation,
    CacheInformation,
    ProcessorSerial,
    CacheParameters,
    MonitorMwait,
    ThermalPowerManagement,
    StructuredExtendedFeature,
    DirectCacheAccess,
    PerformanceMonitoring,
    ExtendedTopology,
    ProcessorExtendedState,
    QualityofService,
    ExtendedFunction,
}

struct LeafData(CpuIdLeaf, &'static str, u32);

const LEAF_INFORMATION: [LeafData; 14] = [
    LeafData(CpuIdLeaf::VendorInformation, "GenuineIntel", 0x0),
    LeafData(CpuIdLeaf::FeatureInformation, "Version/Feature Information", 0x1),
    LeafData(CpuIdLeaf::CacheInformation, "Cache and TLB Information", 0x2),
    LeafData(CpuIdLeaf::ProcessorSerial, "Processor serial number", 0x3),
    LeafData(CpuIdLeaf::CacheParameters, "Deterministic Cache Parameters", 0x4),
    LeafData(CpuIdLeaf::MonitorMwait, "MONITOR/MWAIT", 0x5),
    LeafData(CpuIdLeaf::ThermalPowerManagement, "Thermal and Power Management", 0x6),
    LeafData(CpuIdLeaf::StructuredExtendedFeature, "Structured Extended Feature Flags", 0x7),
    LeafData(CpuIdLeaf::DirectCacheAccess, "Direct Cache Access Information", 0x9),
    LeafData(CpuIdLeaf::PerformanceMonitoring, "Architectural Performance Monitoring", 0xA),
    LeafData(CpuIdLeaf::ExtendedTopology, "Extended Topology Enumeration", 0xB),
    LeafData(CpuIdLeaf::ProcessorExtendedState, "Processor Extended State Enumeration", 0xD),
    LeafData(CpuIdLeaf::QualityofService, "Quality of Service", 0xF),
    LeafData(CpuIdLeaf::ExtendedFunction, "Extended Function CPUID Information", 0x80000000),
];

#[derive(Debug)]
pub struct CpuId;

#[derive(Debug, Copy, Clone)]
pub struct CpuIdResult {
    pub eax: u32,
    pub ebx: u32,
    pub ecx: u32,
    pub edx: u32
}

impl CpuId {

    pub fn get_vendor_information(&self) -> CpuIdVendorInfo {
        let res = cpuid!(0);
        CpuIdVendorInfo{ebx: res.ebx, ecx: res.ecx, edx: res.edx}
    }

    pub fn get_feature_information(&self) -> CpuIdFeatureInfo {
        let res = cpuid!(1);
        CpuIdFeatureInfo{eax: res.eax, ebx: res.ebx, ecx: FeatureInfoEcx { bits: res.ecx }, edx: res.edx}
    }

}

pub struct CpuIdVendorInfo {
    pub ebx: u32,
    pub ecx: u32,
    pub edx: u32
}

fn as_bytes(v: &u32) -> &[u8] {
    let start = v as *const u32 as *const u8;
    unsafe { slice::from_raw_parts(start, 4) }
}

impl fmt::Display for CpuIdVendorInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            write!(f, "{}{}{}",
               str::from_utf8_unchecked(as_bytes(&self.ebx)),
               str::from_utf8_unchecked(as_bytes(&self.edx)),
               str::from_utf8_unchecked(as_bytes(&self.ecx)))
        }
    }
}

pub struct CpuIdFeatureInfo {
    pub eax: u32,
    pub ebx: u32,
    pub ecx: FeatureInfoEcx,
    pub edx: u32
}

bitflags! {
    flags FeatureInfoEcx: u32 {
        const FINFO_ECX_SSE3 = 1 << 0,
        const FINFO_ECX_PCLMULQDQ = 1 << 1,
        const FINFO_ECX_DTES64 = 1 << 2,
        const FINFO_ECX_MONITOR = 1 << 3,
        const FINFO_ECX_DS_CPL = 1 << 4,
        const FINFO_ECX_VMX  = 1 << 5,
        const FINFO_ECX_SMX = 1 << 6,
        const FINFO_ECX_EIST = 1 << 7,
        const FINFO_ECX_TM2 = 1 << 8,
        const FINFO_ECX_SSSE3 = 1 << 9,
        const FINFO_ECX_CNXT_ID = 1 << 10,
        const FINFO_ECX_SDBG  = 1 << 11,
        const FINFO_ECX_FMA = 1 << 12,
        const FINFO_ECX_CMPXCHG16B = 1 << 13,
        const FINFO_ECX_XTPR = 1 << 14,
        const FINFO_ECX_PDCM = 1 << 15,
        const FINFO_ECX_PCID = 1 << 17,
        const FINFO_ECX_DCA = 1 << 18,
        const FINFO_ECX_SSE41 = 1 << 19,
        const FINFO_ECX_SSE42 = 1 << 20,
        const FINFO_ECX_X2APIC = 1 << 21,
        const FINFO_ECX_MOVBE = 1 << 22,
        const FINFO_ECX_POPCNT = 1 << 23,
        const FINFO_ECX_TSC_DEADLINE = 1 << 24,
        const FINFO_ECX_AESNI = 1 << 25,
        const FINFO_ECX_XSAVE = 1 << 26,
        const FINFO_ECX_OSXSAVE = 1 << 27,
        const FINFO_ECX_AVX = 1 << 28
    }
}

impl CpuIdFeatureInfo {

    pub fn get_extended_family_id(&self) -> u8 {
        ((self.eax >> 20) & 0xff) as u8
    }

    pub fn get_extended_model_id(&self) -> u8 {
        ((self.eax >> 16) & 0b1111) as u8
    }

    pub fn get_family_id(&self) -> u8 {
        ((self.eax >> 8) & 0b1111) as u8
    }

    pub fn get_model(&self) -> u8 {
        ((self.eax >> 4) & 0b1111) as u8
    }

    pub fn get_stepping_id(&self) -> u8 {
        ((self.eax & 0b1111)) as u8
    }

    pub fn get_brand_index(&self) -> u8 {
        (self.ebx) as u8
    }

    pub fn get_cflush_cache_line_size(&self) -> u8 {
        ((self.ebx >> 8)) as u8
    }

    pub fn get_local_apic_id(&self) -> u8 {
        ((self.ebx >> 24)) as u8
    }
}

#[cfg(test)]
#[test]
fn genuine_intel() {
    let cpu: CpuId = CpuId;
    let vinfo = cpu.get_vendor_information();
    println!("{}", vinfo);

    // GenuineIntel
    assert!(vinfo.ebx == 0x756e6547);
    assert!(vinfo.edx == 0x49656e69);
    assert!(vinfo.ecx == 0x6c65746e);
}

#[cfg(test)]
#[test]
fn feature_info() {
    let cpu: CpuId = CpuId;
    let finfo = cpu.get_feature_information();

    println!("{}", finfo.get_extended_family_id());
    println!("{}", finfo.get_extended_model_id());
    println!("{}", finfo.get_family_id());
    println!("{}", finfo.get_model());
    println!("{}", finfo.get_stepping_id());
    println!("{}", finfo.get_brand_index());
    println!("{}", finfo.get_cflush_cache_line_size());
    println!("{}", finfo.get_local_apic_id());
}<commit_msg>Update feature.<commit_after>#![feature(no_std, core, core_prelude, asm, raw, prelude_import)]
#![no_std]

#![crate_name = "raw_cpuid"]
#![crate_type = "lib"]

#[macro_use]
extern crate core;

#[macro_use]
extern crate bitflags;

#[cfg(test)]
#[macro_use]
extern crate std;

#[cfg(test)]
#[prelude_import]
use std::prelude::v1::*;

use core::prelude::*;
use core::iter;
use core::raw;
use core::str;
use core::mem::{transmute};
use core::fmt;
use core::slice;

#[cfg(not(test))]
mod std {
    pub use core::ops;
    pub use core::option;
}

const MAX_ENTRIES: usize = 32;

#[macro_export]
macro_rules! cpuid {
    ($eax:expr)
        => ( $crate::cpuid1($eax as u32) );

    ($eax:expr, $ecx:expr)
        => ( $crate::cpuid2($eax as u32, $ecx as u32) );

}

fn cpuid2(eax: u32, ecx: u32) -> CpuIdResult {
    let mut res = CpuIdResult{eax: 0, ebx: 0, ecx: 0, edx: 0};

    unsafe {
        asm!("movl $0, %eax" : : "r" (eax) : "eax");
        asm!("movl $0, %ecx" : : "r" (ecx) : "ecx");
        asm!("cpuid" : "={eax}"(res.eax) "={ebx}"(res.ebx)
                       "={ecx}"(res.ecx) "={edx}"(res.edx)
                     :: "eax", "ebx", "ecx", "edx");
    }

    res
}

fn cpuid1(eax: u32) -> CpuIdResult {
    let mut res = CpuIdResult{eax: 0, ebx: 0, ecx: 0, edx: 0};

    unsafe {
        asm!("movl $0, %eax" : : "r" (eax) : "eax");
        asm!("cpuid" : "={eax}"(res.eax) "={ebx}"(res.ebx)
                       "={ecx}"(res.ecx) "={edx}"(res.edx)
                     :: "eax", "ebx", "ecx", "edx");
    }

    res
}

enum CpuIdLeaf {
    VendorInformation,
    FeatureInformation,
    CacheInformation,
    ProcessorSerial,
    CacheParameters,
    MonitorMwait,
    ThermalPowerManagement,
    StructuredExtendedFeature,
    DirectCacheAccess,
    PerformanceMonitoring,
    ExtendedTopology,
    ProcessorExtendedState,
    QualityofService,
    ExtendedFunction,
}

struct LeafData(CpuIdLeaf, &'static str, u32);

const LEAF_INFORMATION: [LeafData; 14] = [
    LeafData(CpuIdLeaf::VendorInformation, "GenuineIntel", 0x0),
    LeafData(CpuIdLeaf::FeatureInformation, "Version/Feature Information", 0x1),
    LeafData(CpuIdLeaf::CacheInformation, "Cache and TLB Information", 0x2),
    LeafData(CpuIdLeaf::ProcessorSerial, "Processor serial number", 0x3),
    LeafData(CpuIdLeaf::CacheParameters, "Deterministic Cache Parameters", 0x4),
    LeafData(CpuIdLeaf::MonitorMwait, "MONITOR/MWAIT", 0x5),
    LeafData(CpuIdLeaf::ThermalPowerManagement, "Thermal and Power Management", 0x6),
    LeafData(CpuIdLeaf::StructuredExtendedFeature, "Structured Extended Feature Flags", 0x7),
    LeafData(CpuIdLeaf::DirectCacheAccess, "Direct Cache Access Information", 0x9),
    LeafData(CpuIdLeaf::PerformanceMonitoring, "Architectural Performance Monitoring", 0xA),
    LeafData(CpuIdLeaf::ExtendedTopology, "Extended Topology Enumeration", 0xB),
    LeafData(CpuIdLeaf::ProcessorExtendedState, "Processor Extended State Enumeration", 0xD),
    LeafData(CpuIdLeaf::QualityofService, "Quality of Service", 0xF),
    LeafData(CpuIdLeaf::ExtendedFunction, "Extended Function CPUID Information", 0x80000000),
];

#[derive(Debug)]
pub struct CpuId;

#[derive(Debug, Copy, Clone)]
pub struct CpuIdResult {
    pub eax: u32,
    pub ebx: u32,
    pub ecx: u32,
    pub edx: u32
}

impl CpuId {

    pub fn get_vendor_information(&self) -> CpuIdVendorInfo {
        let res = cpuid!(0);
        CpuIdVendorInfo{ebx: res.ebx, ecx: res.ecx, edx: res.edx}
    }

    pub fn get_feature_information(&self) -> CpuIdFeatureInfo {
        let res = cpuid!(1);
        CpuIdFeatureInfo{eax: res.eax, ebx: res.ebx, ecx: FeatureInfoEcx { bits: res.ecx }, edx: res.edx}
    }

}

pub struct CpuIdVendorInfo {
    pub ebx: u32,
    pub ecx: u32,
    pub edx: u32
}

fn as_bytes(v: &u32) -> &[u8] {
    let start = v as *const u32 as *const u8;
    unsafe { slice::from_raw_parts(start, 4) }
}

impl fmt::Display for CpuIdVendorInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        unsafe {
            write!(f, "{}{}{}",
               str::from_utf8_unchecked(as_bytes(&self.ebx)),
               str::from_utf8_unchecked(as_bytes(&self.edx)),
               str::from_utf8_unchecked(as_bytes(&self.ecx)))
        }
    }
}

pub struct CpuIdFeatureInfo {
    pub eax: u32,
    pub ebx: u32,
    pub ecx: FeatureInfoEcx,
    pub edx: u32
}

bitflags! {
    flags FeatureInfoEcx: u32 {
        const FINFO_ECX_SSE3 = 1 << 0,
        const FINFO_ECX_PCLMULQDQ = 1 << 1,
        const FINFO_ECX_DTES64 = 1 << 2,
        const FINFO_ECX_MONITOR = 1 << 3,
        const FINFO_ECX_DS_CPL = 1 << 4,
        const FINFO_ECX_VMX  = 1 << 5,
        const FINFO_ECX_SMX = 1 << 6,
        const FINFO_ECX_EIST = 1 << 7,
        const FINFO_ECX_TM2 = 1 << 8,
        const FINFO_ECX_SSSE3 = 1 << 9,
        const FINFO_ECX_CNXT_ID = 1 << 10,
        const FINFO_ECX_SDBG  = 1 << 11,
        const FINFO_ECX_FMA = 1 << 12,
        const FINFO_ECX_CMPXCHG16B = 1 << 13,
        const FINFO_ECX_XTPR = 1 << 14,
        const FINFO_ECX_PDCM = 1 << 15,
        const FINFO_ECX_PCID = 1 << 17,
        const FINFO_ECX_DCA = 1 << 18,
        const FINFO_ECX_SSE41 = 1 << 19,
        const FINFO_ECX_SSE42 = 1 << 20,
        const FINFO_ECX_X2APIC = 1 << 21,
        const FINFO_ECX_MOVBE = 1 << 22,
        const FINFO_ECX_POPCNT = 1 << 23,
        const FINFO_ECX_TSC_DEADLINE = 1 << 24,
        const FINFO_ECX_AESNI = 1 << 25,
        const FINFO_ECX_XSAVE = 1 << 26,
        const FINFO_ECX_OSXSAVE = 1 << 27,
        const FINFO_ECX_AVX = 1 << 28
    }
}

impl CpuIdFeatureInfo {

    pub fn get_extended_family_id(&self) -> u8 {
        ((self.eax >> 20) & 0xff) as u8
    }

    pub fn get_extended_model_id(&self) -> u8 {
        ((self.eax >> 16) & 0b1111) as u8
    }

    pub fn get_family_id(&self) -> u8 {
        ((self.eax >> 8) & 0b1111) as u8
    }

    pub fn get_model(&self) -> u8 {
        ((self.eax >> 4) & 0b1111) as u8
    }

    pub fn get_stepping_id(&self) -> u8 {
        ((self.eax & 0b1111)) as u8
    }

    pub fn get_brand_index(&self) -> u8 {
        (self.ebx) as u8
    }

    pub fn get_cflush_cache_line_size(&self) -> u8 {
        ((self.ebx >> 8)) as u8
    }

    pub fn get_local_apic_id(&self) -> u8 {
        ((self.ebx >> 24)) as u8
    }
}

#[cfg(test)]
#[test]
fn genuine_intel() {
    let cpu: CpuId = CpuId;
    let vinfo = cpu.get_vendor_information();
    println!("{}", vinfo);

    // GenuineIntel
    assert!(vinfo.ebx == 0x756e6547);
    assert!(vinfo.edx == 0x49656e69);
    assert!(vinfo.ecx == 0x6c65746e);
}

#[cfg(test)]
#[test]
fn feature_info() {
    let cpu: CpuId = CpuId;
    let finfo = cpu.get_feature_information();

    println!("{}", finfo.get_extended_family_id());
    println!("{}", finfo.get_extended_model_id());
    println!("{}", finfo.get_family_id());
    println!("{}", finfo.get_model());
    println!("{}", finfo.get_stepping_id());
    println!("{}", finfo.get_brand_index());
    println!("{}", finfo.get_cflush_cache_line_size());
    println!("{}", finfo.get_local_apic_id());
}<|endoftext|>


<commit_before>//! This crate parses and processes a stream of data that contains
//! `multipart/form-data` content.
//!
//! The main entry point is `parse_multipart`

extern crate httparse;
extern crate hyper;
extern crate libc;
#[macro_use]
extern crate mime;
extern crate tempdir;
extern crate textnonce;
#[macro_use]
extern crate log;

pub mod buf;
pub mod error;
mod headers;
#[cfg(test)]
mod mock;

use std::path::PathBuf;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};

use hyper::header::{ContentType, Headers};
use mime::{Attr, Mime, Param, SubLevel, TopLevel, Value};
use tempdir::TempDir;
use textnonce::TextNonce;

use buf::BufReadExt;
pub use error::Error;
use headers::ContentDisposition;

/// An uploaded file that was received as part of `multipart/form-data` parsing.
///
/// Files are streamed to disk because they may not fit in memory.
#[derive(Clone, Debug, PartialEq)]
pub struct UploadedFile {
    /// The temporary file where the data was saved.
    pub path: PathBuf,
    /// The filename that was specified in the data, unfiltered. It may or may not be legal on the
    /// local filesystem.
    pub filename: Option<String>,
    /// The unvalidated content-type that was specified in the data.
    pub content_type: Mime,
    /// The size of the file.
    pub size: usize,
}

/// The extracted text fields and uploaded files from a `multipart/form-data` request.
///
/// Use `parse_multipart` to devise this object from a request.
#[derive(Clone, Debug, PartialEq)]
pub struct FormData {
    /// Name-value pairs for plain text fields. Technically, these are form data parts with no
    /// filename specified in the part's `Content-Disposition`.
    pub fields: Vec<(String, String)>,
    /// Name-value pairs for temporary files. Technically, these are form data parts with a filename
    /// specified in the part's `Content-Disposition`.
    pub files: Vec<(String, UploadedFile)>,
}

impl FormData {
    pub fn new() -> FormData {
        FormData { fields: vec![], files: vec![] }
    }
}

/// Parses and processes a stream of `multipart/form-data` content.
///
/// The request is streamed, and this function saves embedded uploaded files to disk as they are
/// encountered by the parser.
pub fn parse_multipart<S: Read>(stream: &mut S, boundary: String) -> Result<FormData, Error> {
    let mut reader = BufReader::with_capacity(4096, stream);
    let mut form_data = FormData::new();
    try!(run_state_machine(boundary, &mut reader, &mut form_data, MultipartSubLevel::FormData));
    Ok(form_data)
}

// A state in the parsing state machine.
enum State {
    // Discard until after initial boundary and CRLF.
    Discarding,
    // Capture headers to blank line.
    ReadingHeaders,
    // Capture entire `multipart/mixed` body until boundary, then discard past CRLF.
    CapturingMixed(ContentDisposition, ContentType),
    // Capture value until boundary, then discard past CRLF.
    CapturingValue(ContentDisposition),
    // Capture file until boundary, then discard past CRLF.
    CapturingFile(ContentDisposition, Option<ContentType>),
}

// A multipart MIME parsing mode.
#[derive(PartialEq)]
enum MultipartSubLevel {
    // Represents `multipart/form-data`.
    FormData,
    // Represents `multipart/mixed` with a `name` key.
    Mixed(String),
}

// Parse either a `multipart/form-data` or `multipart/mixed` MIME body.
fn run_state_machine<R: BufRead>(boundary: String, reader: &mut R, form_data: &mut FormData,
                                 mode: MultipartSubLevel) -> Result<(), Error> {
    use State::*;
    use MultipartSubLevel::*;

    let boundary = boundary.into_bytes();
    let crlf_boundary = crlf_boundary(&boundary);
    let mut state = Discarding;

    loop {
        let mut buf: Vec<u8> = Vec::new();

        match state {
            Discarding => {
                // Read up to and including the boundary.
                let read = try!(reader.stream_until_token(&boundary, &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }

                state = ReadingHeaders;
            },
            ReadingHeaders => {
                {
                    // If the next two lookahead characters are '--', parsing is finished.
                    let peeker = try!(reader.fill_buf());
                    if peeker.len() >= 2 && &peeker[..2] == b"--" {
                        return Ok(());
                    }
                }

                // Read up to and including the CRLF after the boundary.
                let read = try!(reader.stream_until_token(b"\r\n", &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }

                buf.truncate(0);
                let read = try!(reader.stream_until_token(b"\r\n\r\n", &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }
                // `parse_headers` requires this token at the end:
                buf.extend(b"\r\n\r\n".iter().map(|&i| i));

                // Parse the headers.
                let mut header_memory = [httparse::EMPTY_HEADER; 4];
                match httparse::parse_headers(&buf, &mut header_memory) {
                    Ok(httparse::Status::Complete((_, raw_headers))) => {
                        // Turn raw headers into hyper headers.
                        let headers = try!(Headers::from_raw(raw_headers));

                        let cd: &ContentDisposition = match headers.get() {
                            Some(cd) => cd,
                            None => return Err(Error::MissingDisposition),
                        };

                        let ct: Option<&ContentType> = headers.get();

                        match mode {
                            FormData if cd.disposition == "form-data" => {
                                state = if is_multipart_mixed(ct) {
                                    CapturingMixed(cd.clone(), ct.unwrap().clone())
                                } else if ct.is_some() || cd.filename.is_some() {
                                    CapturingFile(cd.clone(), ct.map(|ct| ct.clone()))
                                } else {
                                    CapturingValue(cd.clone())
                                };
                            },
                            Mixed(_) if cd.disposition == "file" => {
                                state = CapturingFile(cd.clone(), ct.map(|ct| ct.clone()));
                            },
                            _ => return Err(Error::InvalidDisposition),
                        }
                    },
                    Ok(httparse::Status::Partial) => return Err(Error::PartialHeaders),
                    Err(err) => return Err(From::from(err)),
                }
            },
            CapturingMixed(cd, ct) => {
                let boundary = try!(get_boundary_token(&(ct.0).2));
                let mode = Mixed(try!(cd.name.ok_or(Error::NoName)));
                try!(run_state_machine(boundary, reader, form_data, mode));
                state = Discarding;
            },
            CapturingValue(_) if mode != FormData => unreachable!(),
            CapturingValue(cd) => {
                buf.truncate(0);
                let _ = try!(reader.stream_until_token(&crlf_boundary, &mut buf));

                let key = try!(cd.name.ok_or(Error::NoName));
                let val = try!(String::from_utf8(buf));

                form_data.fields.push((key, val));

                state = ReadingHeaders;
            },
            CapturingFile(cd, ct) => {
                // Setup a file to capture the contents.
                let mut path = try!(TempDir::new("formdata")).into_path();
                path.push(TextNonce::sized_urlsafe(32).unwrap().into_string());
                let mut file = try!(File::create(path.clone()));

                // Stream out the file.
                let read = try!(reader.stream_until_token(&crlf_boundary, &mut file));

                // TODO: Handle Content-Transfer-Encoding.

                let key = match mode {
                    Mixed(ref name) => name.clone(),
                    FormData => try!(cd.name.ok_or(Error::NoName)),
                };

                let file = UploadedFile {
                    path: path,
                    filename: cd.filename.clone(),
                    content_type: ct.map_or(mime!(Text/Plain; Charset=Utf8), |ct| ct.0),
                    size: read - crlf_boundary.len()
                };

                form_data.files.push((key, file));
                state = ReadingHeaders;
            },
        }
    }
}

fn is_multipart_mixed(ct: Option<&ContentType>) -> bool {
    let ct = match ct {
        Some(ct) => ct,
        None => return false,
    };

    let ContentType(ref mime) = *ct;
    let Mime(ref top_level, ref sub_level, _) = *mime;

    if *top_level != TopLevel::Multipart {
        return false;
    }

    match *sub_level {
        SubLevel::Ext(ref ext) => ext == "mixed",
        _ => false,
    }
}

/// Get the `multipart/form-data` boundary string from hyper::Headers
pub fn get_multipart_boundary(headers: &Headers) -> Result<String, Error> {
    // Verify that the request is 'Content-Type: multipart/form-data'.
    let ct: &ContentType = match headers.get() {
        Some(ct) => ct,
        None => return Err(Error::NoRequestContentType),
    };
    let ContentType(ref mime) = *ct;
    let Mime(ref top_level, ref sub_level, ref params) = *mime;

    if *top_level != TopLevel::Multipart {
        return Err(Error::NotMultipart);
    }

    if *sub_level != SubLevel::FormData {
        return Err(Error::NotFormData);
    }

    get_boundary_token(params)
}

fn get_boundary_token(params: &[Param]) -> Result<String, Error> {
    for &(ref attr, ref val) in params.iter() {
        if let (&Attr::Boundary, &Value::Ext(ref val)) = (attr, val) {
            return Ok(format!("--{}", val.clone()));
        }
    }

    Err(Error::BoundaryNotSpecified)
}

fn crlf_boundary(boundary: &Vec<u8>) -> Vec<u8> {
    let mut crlf_boundary = Vec::with_capacity(2 + boundary.len());
    crlf_boundary.extend(b"\r\n".iter().map(|&i| i));
    crlf_boundary.extend(boundary.clone());
    crlf_boundary
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::net::SocketAddr;

    use hyper::buffer::BufReader;
    use hyper::net::NetworkStream;
    use hyper::server::Request as HyperRequest;

    use mock::MockStream;

    #[test]
    fn parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field1\"\r\n\
                      \r\n\
                      data1\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field2\"; filename=\"image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      \r\n\
                      This is a file\r\n\
                      with two lines\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field3\"; filename=\"file.txt\"\r\n\
                      \r\n\
                      This is a file\r\n\
                      --abcdefg--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let mut req = HyperRequest::new(&mut stream, sock).unwrap();
        let boundary = get_multipart_boundary(&req.headers).unwrap();

        match parse_multipart(&mut req, boundary) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "field1" {
                        assert_eq!(&val, "data1");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                for (key, file) in form_data.files {
                    if &key == "field2" {
                        assert_eq!(file.size, 30);
                        assert_eq!(&file.filename.unwrap(), "image.gif");
                        assert_eq!(file.content_type, mime!(Image/Gif));
                    } else if &key == "field3" {
                        assert_eq!(file.size, 14);
                        assert_eq!(&file.filename.unwrap(), "file.txt");
                        assert_eq!(file.content_type, mime!(Text/Plain; Charset=Utf8));
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }

    #[test]
    fn mixed_parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=AaB03x\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --AaB03x\r\n\
                      Content-Disposition: form-data; name=\"submit-name\"\r\n\
                      \r\n\
                      Larry\r\n\
                      --AaB03x\r\n\
                      Content-Disposition: form-data; name=\"files\"\r\n\
                      Content-Type: multipart/mixed; boundary=BbC04y\r\n\
                      \r\n\
                      --BbC04y\r\n\
                      Content-Disposition: file; filename=\"file1.txt\"\r\n\
                      \r\n\
                      ... contents of file1.txt ...\r\n\
                      --BbC04y\r\n\
                      Content-Disposition: file; filename=\"awesome_image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      Content-Transfer-Encoding: binary\r\n\
                      \r\n\
                      ... contents of awesome_image.gif ...\r\n\
                      --BbC04y--\r\n\
                      --AaB03x--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let mut req = HyperRequest::new(&mut stream, sock).unwrap();
        let boundary = get_multipart_boundary(&req.headers).unwrap();

        match parse_multipart(&mut req, boundary) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "submit-name" {
                        assert_eq!(&val, "Larry");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                for (key, file) in form_data.files {
                    assert_eq!(&key, "files");
                    match &file.filename.unwrap()[..] {
                        "file1.txt" => {
                            assert_eq!(file.size, 29);
                            assert_eq!(file.content_type, mime!(Text/Plain; Charset=Utf8));
                        }
                        "awesome_image.gif" => {
                            assert_eq!(file.size, 37);
                            assert_eq!(file.content_type, mime!(Image/Gif));
                        },
                        _ => unreachable!(),
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }
}
<commit_msg>Clean up temporary files [breaking change]<commit_after>//! This crate parses and processes a stream of data that contains
//! `multipart/form-data` content.
//!
//! The main entry point is `parse_multipart`

extern crate httparse;
extern crate hyper;
extern crate libc;
#[macro_use]
extern crate mime;
extern crate tempdir;
extern crate textnonce;
#[macro_use]
extern crate log;

pub mod buf;
pub mod error;
mod headers;
#[cfg(test)]
mod mock;

use std::path::PathBuf;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::ops::Drop;

use hyper::header::{ContentType, Headers};
use mime::{Attr, Mime, Param, SubLevel, TopLevel, Value};
use tempdir::TempDir;
use textnonce::TextNonce;

use buf::BufReadExt;
pub use error::Error;
use headers::ContentDisposition;

/// An uploaded file that was received as part of `multipart/form-data` parsing.
///
/// Files are streamed to disk because they may not fit in memory.
#[derive(Clone, Debug, PartialEq)]
pub struct UploadedFile {
    /// The temporary file where the data was saved.
    pub path: PathBuf,
    /// The filename that was specified in the data, unfiltered. It may or may not be legal on the
    /// local filesystem.
    pub filename: Option<String>,
    /// The unvalidated content-type that was specified in the data.
    pub content_type: Mime,
    /// The size of the file.
    pub size: usize,
    // The temporary directory the upload was put into, saved for the Drop trait
    tempdir: PathBuf,
}

impl UploadedFile {
    pub fn new(content_type: Mime) -> Result<UploadedFile,Error> {
        // Setup a file to capture the contents.
        let tempdir = try!(TempDir::new("formdata")).into_path();
        let mut path = tempdir.clone();
        path.push(TextNonce::sized_urlsafe(32).unwrap().into_string());
        Ok(UploadedFile {
            path: path,
            filename: None,
            content_type: content_type,
            size: 0,
            tempdir: tempdir,
        })
    }
}

impl Drop for UploadedFile {
    fn drop(&mut self) {
        if ::std::fs::remove_file(&self.path).is_ok() {
            let _ = ::std::fs::remove_dir(&self.tempdir);
        }
    }
}

/// The extracted text fields and uploaded files from a `multipart/form-data` request.
///
/// Use `parse_multipart` to devise this object from a request.
#[derive(Clone, Debug, PartialEq)]
pub struct FormData {
    /// Name-value pairs for plain text fields. Technically, these are form data parts with no
    /// filename specified in the part's `Content-Disposition`.
    pub fields: Vec<(String, String)>,
    /// Name-value pairs for temporary files. Technically, these are form data parts with a filename
    /// specified in the part's `Content-Disposition`.
    pub files: Vec<(String, UploadedFile)>,
}

impl FormData {
    pub fn new() -> FormData {
        FormData { fields: vec![], files: vec![] }
    }
}

/// Parses and processes a stream of `multipart/form-data` content.
///
/// The request is streamed, and this function saves embedded uploaded files to disk as they are
/// encountered by the parser.
pub fn parse_multipart<S: Read>(stream: &mut S, boundary: String) -> Result<FormData, Error> {
    let mut reader = BufReader::with_capacity(4096, stream);
    let mut form_data = FormData::new();
    try!(run_state_machine(boundary, &mut reader, &mut form_data, MultipartSubLevel::FormData));
    Ok(form_data)
}

// A state in the parsing state machine.
enum State {
    // Discard until after initial boundary and CRLF.
    Discarding,
    // Capture headers to blank line.
    ReadingHeaders,
    // Capture entire `multipart/mixed` body until boundary, then discard past CRLF.
    CapturingMixed(ContentDisposition, ContentType),
    // Capture value until boundary, then discard past CRLF.
    CapturingValue(ContentDisposition),
    // Capture file until boundary, then discard past CRLF.
    CapturingFile(ContentDisposition, Option<ContentType>),
}

// A multipart MIME parsing mode.
#[derive(PartialEq)]
enum MultipartSubLevel {
    // Represents `multipart/form-data`.
    FormData,
    // Represents `multipart/mixed` with a `name` key.
    Mixed(String),
}

// Parse either a `multipart/form-data` or `multipart/mixed` MIME body.
fn run_state_machine<R: BufRead>(boundary: String, reader: &mut R, form_data: &mut FormData,
                                 mode: MultipartSubLevel) -> Result<(), Error> {
    use State::*;
    use MultipartSubLevel::*;

    let boundary = boundary.into_bytes();
    let crlf_boundary = crlf_boundary(&boundary);
    let mut state = Discarding;

    loop {
        let mut buf: Vec<u8> = Vec::new();

        match state {
            Discarding => {
                // Read up to and including the boundary.
                let read = try!(reader.stream_until_token(&boundary, &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }

                state = ReadingHeaders;
            },
            ReadingHeaders => {
                {
                    // If the next two lookahead characters are '--', parsing is finished.
                    let peeker = try!(reader.fill_buf());
                    if peeker.len() >= 2 && &peeker[..2] == b"--" {
                        return Ok(());
                    }
                }

                // Read up to and including the CRLF after the boundary.
                let read = try!(reader.stream_until_token(b"\r\n", &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }

                buf.truncate(0);
                let read = try!(reader.stream_until_token(b"\r\n\r\n", &mut buf));
                if read == 0 {
                    return Err(Error::Eof);
                }
                // `parse_headers` requires this token at the end:
                buf.extend(b"\r\n\r\n".iter().map(|&i| i));

                // Parse the headers.
                let mut header_memory = [httparse::EMPTY_HEADER; 4];
                match httparse::parse_headers(&buf, &mut header_memory) {
                    Ok(httparse::Status::Complete((_, raw_headers))) => {
                        // Turn raw headers into hyper headers.
                        let headers = try!(Headers::from_raw(raw_headers));

                        let cd: &ContentDisposition = match headers.get() {
                            Some(cd) => cd,
                            None => return Err(Error::MissingDisposition),
                        };

                        let ct: Option<&ContentType> = headers.get();

                        match mode {
                            FormData if cd.disposition == "form-data" => {
                                state = if is_multipart_mixed(ct) {
                                    CapturingMixed(cd.clone(), ct.unwrap().clone())
                                } else if ct.is_some() || cd.filename.is_some() {
                                    CapturingFile(cd.clone(), ct.map(|ct| ct.clone()))
                                } else {
                                    CapturingValue(cd.clone())
                                };
                            },
                            Mixed(_) if cd.disposition == "file" => {
                                state = CapturingFile(cd.clone(), ct.map(|ct| ct.clone()));
                            },
                            _ => return Err(Error::InvalidDisposition),
                        }
                    },
                    Ok(httparse::Status::Partial) => return Err(Error::PartialHeaders),
                    Err(err) => return Err(From::from(err)),
                }
            },
            CapturingMixed(cd, ct) => {
                let boundary = try!(get_boundary_token(&(ct.0).2));
                let mode = Mixed(try!(cd.name.ok_or(Error::NoName)));
                try!(run_state_machine(boundary, reader, form_data, mode));
                state = Discarding;
            },
            CapturingValue(_) if mode != FormData => unreachable!(),
            CapturingValue(cd) => {
                buf.truncate(0);
                let _ = try!(reader.stream_until_token(&crlf_boundary, &mut buf));

                let key = try!(cd.name.ok_or(Error::NoName));
                let val = try!(String::from_utf8(buf));

                form_data.fields.push((key, val));

                state = ReadingHeaders;
            },
            CapturingFile(cd, ct) => {
                // Setup a file to capture the contents.
                let mut uploaded_file = try!(UploadedFile::new(
                    ct.map_or(mime!(Text/Plain; Charset=Utf8), |ct| ct.0)));
                uploaded_file.filename = cd.filename.clone();
                let mut file = try!(File::create(uploaded_file.path.clone()));

                // Stream out the file.
                let read = try!(reader.stream_until_token(&crlf_boundary, &mut file));
                uploaded_file.size = read - crlf_boundary.len();

                // TODO: Handle Content-Transfer-Encoding.

                let key = match mode {
                    Mixed(ref name) => name.clone(),
                    FormData => try!(cd.name.ok_or(Error::NoName)),
                };

                form_data.files.push((key, uploaded_file));
                state = ReadingHeaders;
            },
        }
    }
}

fn is_multipart_mixed(ct: Option<&ContentType>) -> bool {
    let ct = match ct {
        Some(ct) => ct,
        None => return false,
    };

    let ContentType(ref mime) = *ct;
    let Mime(ref top_level, ref sub_level, _) = *mime;

    if *top_level != TopLevel::Multipart {
        return false;
    }

    match *sub_level {
        SubLevel::Ext(ref ext) => ext == "mixed",
        _ => false,
    }
}

/// Get the `multipart/form-data` boundary string from hyper::Headers
pub fn get_multipart_boundary(headers: &Headers) -> Result<String, Error> {
    // Verify that the request is 'Content-Type: multipart/form-data'.
    let ct: &ContentType = match headers.get() {
        Some(ct) => ct,
        None => return Err(Error::NoRequestContentType),
    };
    let ContentType(ref mime) = *ct;
    let Mime(ref top_level, ref sub_level, ref params) = *mime;

    if *top_level != TopLevel::Multipart {
        return Err(Error::NotMultipart);
    }

    if *sub_level != SubLevel::FormData {
        return Err(Error::NotFormData);
    }

    get_boundary_token(params)
}

fn get_boundary_token(params: &[Param]) -> Result<String, Error> {
    for &(ref attr, ref val) in params.iter() {
        if let (&Attr::Boundary, &Value::Ext(ref val)) = (attr, val) {
            return Ok(format!("--{}", val.clone()));
        }
    }

    Err(Error::BoundaryNotSpecified)
}

fn crlf_boundary(boundary: &Vec<u8>) -> Vec<u8> {
    let mut crlf_boundary = Vec::with_capacity(2 + boundary.len());
    crlf_boundary.extend(b"\r\n".iter().map(|&i| i));
    crlf_boundary.extend(boundary.clone());
    crlf_boundary
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::net::SocketAddr;

    use hyper::buffer::BufReader;
    use hyper::net::NetworkStream;
    use hyper::server::Request as HyperRequest;

    use mock::MockStream;

    #[test]
    fn parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field1\"\r\n\
                      \r\n\
                      data1\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field2\"; filename=\"image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      \r\n\
                      This is a file\r\n\
                      with two lines\r\n\
                      --abcdefg\r\n\
                      Content-Disposition: form-data; name=\"field3\"; filename=\"file.txt\"\r\n\
                      \r\n\
                      This is a file\r\n\
                      --abcdefg--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let mut req = HyperRequest::new(&mut stream, sock).unwrap();
        let boundary = get_multipart_boundary(&req.headers).unwrap();

        match parse_multipart(&mut req, boundary) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "field1" {
                        assert_eq!(&val, "data1");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                for (key, file) in form_data.files {
                    if &key == "field2" {
                        assert_eq!(file.size, 30);
                        assert_eq!(file.filename.as_ref().unwrap(), "image.gif");
                        assert_eq!(file.content_type, mime!(Image/Gif));
                    } else if &key == "field3" {
                        assert_eq!(file.size, 14);
                        assert_eq!(file.filename.as_ref().unwrap(), "file.txt");
                        assert_eq!(file.content_type, mime!(Text/Plain; Charset=Utf8));
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }

    #[test]
    fn mixed_parser() {
        let input = b"POST / HTTP/1.1\r\n\
                      Host: example.domain\r\n\
                      Content-Type: multipart/form-data; boundary=AaB03x\r\n\
                      Content-Length: 1000\r\n\
                      \r\n\
                      --AaB03x\r\n\
                      Content-Disposition: form-data; name=\"submit-name\"\r\n\
                      \r\n\
                      Larry\r\n\
                      --AaB03x\r\n\
                      Content-Disposition: form-data; name=\"files\"\r\n\
                      Content-Type: multipart/mixed; boundary=BbC04y\r\n\
                      \r\n\
                      --BbC04y\r\n\
                      Content-Disposition: file; filename=\"file1.txt\"\r\n\
                      \r\n\
                      ... contents of file1.txt ...\r\n\
                      --BbC04y\r\n\
                      Content-Disposition: file; filename=\"awesome_image.gif\"\r\n\
                      Content-Type: image/gif\r\n\
                      Content-Transfer-Encoding: binary\r\n\
                      \r\n\
                      ... contents of awesome_image.gif ...\r\n\
                      --BbC04y--\r\n\
                      --AaB03x--";

        let mut mock = MockStream::with_input(input);

        let mock: &mut NetworkStream = &mut mock;
        let mut stream = BufReader::new(mock);
        let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();
        let mut req = HyperRequest::new(&mut stream, sock).unwrap();
        let boundary = get_multipart_boundary(&req.headers).unwrap();

        match parse_multipart(&mut req, boundary) {
            Ok(form_data) => {
                assert_eq!(form_data.fields.len(), 1);
                for (key, val) in form_data.fields {
                    if &key == "submit-name" {
                        assert_eq!(&val, "Larry");
                    }
                }

                assert_eq!(form_data.files.len(), 2);
                for (key, file) in form_data.files {
                    assert_eq!(&key, "files");
                    match &file.filename.as_ref().unwrap()[..] {
                        "file1.txt" => {
                            assert_eq!(file.size, 29);
                            assert_eq!(file.content_type, mime!(Text/Plain; Charset=Utf8));
                        }
                        "awesome_image.gif" => {
                            assert_eq!(file.size, 37);
                            assert_eq!(file.content_type, mime!(Image/Gif));
                        },
                        _ => unreachable!(),
                    }
                }
            },
            Err(err) => panic!("{}", err),
        }
    }
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate unicase;

use unicase::UniCase;
use iron::prelude::*;
use iron::method::Method;
use iron::status;
use iron::headers::{Origin,
					ContentType,
                    AccessControlRequestMethod,
                    AccessControlAllowOrigin,
                    AccessControlAllowHeaders,
                    AccessControlMaxAge,
                    AccessControlAllowMethods};
use iron::middleware::{AroundMiddleware, Handler};

struct CorsMiddleware;
impl AroundMiddleware for CorsMiddleware {

    fn around(self, handler: Box<Handler>) -> Box<Handler> {
        Box::new(move | req: &mut Request | {
            let allowed_headers: Vec<unicase::UniCase<String>> = vec![
                    // To allow application/json
                    UniCase("Content-Type".to_owned()),
                    // Set by some js libs
                    UniCase("X-Requested-With".to_owned()),
                    ];
            let allowed_methods: Vec<Method> = vec![ Method::Get, Method::Put, Method::Post ];
            if req.method == Method::Options &&
                req.headers.get::<AccessControlRequestMethod>().is_some() {
                // Preflight request
                // 1.If the Origin header is not present terminate this set of steps. The request is
                // outside the scope of this specification.
                let maybe_origin = req.headers.get::<Origin>();
                if maybe_origin.is_none() {
                    let resp = Response::with((status::BadRequest,
                                               "Preflight request without Origin header"));
                    return Ok(resp);
                }
                let origin = maybe_origin.unwrap();
                let mut res = Response::with((status::NoContent));
                //
                // 2.If the value of the Origin header is not a case-sensitive match for any of the
                // values in list of origins do not set any additional headers and terminate this
                // set of steps.
                //
                // Note: Always matching is acceptable since the list of origins can be unbounded.
                //
                // Note: The Origin header can only contain a single origin as the user agent
                //       will not follow redirects.
                //
                // 3. Let method be the value as result of parsing the Access-Control-Request-Method
                // header.
                //
                // If there is no Access-Control-Request-Method header or if parsing failed, do not
                // set any additional headers and terminate this set of steps. The request is
                // outside the scope of this specification.
                //
                // 4. Let header field-names be the values as result of parsing the
                // Access-Control-Request-Headers headers.
                //
                // If there are no Access-Control-Request-Headers headers let header field-names be
                // the empty list.
                //
                // If parsing failed do not set any additional headers and terminate this set of
                // steps. The request is outside the scope of this specification.
                //
                // 5.If method is not a case-sensitive match for any of the values in list of
                //   methods do not set any additional headers and terminate this set of steps.
                //
                // Always matching is acceptable since the list of methods can be unbounded.
                //
                // 6. If any of the header field-names is not a ASCII case-insensitive match for any
                // of the values in list of headers do not set any additional headers and terminate
                // this set of steps.
                //
                // Always matching is acceptable since the list of headers can be unbounded.
                //
                // 7. If the resource supports credentials add a single Access-Control-Allow-Origin
                // header, with the value of the Origin header as value, and add a single
                // Access-Control-Allow-Credentials header with the case-sensitive string "true" as
                // value.
                //
                // Otherwise, add a single Access-Control-Allow-Origin header, with either the
                // value of the Origin header or the string "*" as value.
                res.headers.set(AccessControlAllowOrigin::Value(format!("{}", origin)));
                //
                // The string "*" cannot be used for a resource that supports credentials.
                //
                // 8. Optionally add a single Access-Control-Max-Age header with as value the amount
                // of seconds the user agent is allowed to cache the result of the request.
                res.headers.set(AccessControlMaxAge(60*60));
                //
                // 9. If method is a simple method this step may be skipped.
                //
                // Add one or more Access-Control-Allow-Methods headers consisting of (a subset of)
                // the list of methods.
                res.headers.set(AccessControlAllowMethods(allowed_methods));
                //
                // If a method is a simple method it does not need to be listed, but this is not
                // prohibited.
                //
                // Since the list of methods can be unbounded, simply returning the method
                // indicated by Access-Control-Request-Method (if supported) can be enough.
                //
                // 10.If each of the header field-names is a simple header and none is Content-Type,
                // this step may be skipped.
                //
                // Add one or more Access-Control-Allow-Headers headers consisting of (a subset of)
                // the list of headers.
                res.headers.set(AccessControlAllowHeaders(allowed_headers));
                //
                // If a header field name is a simple header and is not Content-Type, it is not
                // required to be listed. Content-Type is to be listed as only a subset of its
                // values makes it qualify as simple header.
                //
                // Since the list of headers can be unbounded, simply returning supported headers
                // from Access-Control-Allow-Headers can be enough.
                Ok(res)
            } else {
                // Normal request
                // 1.If the Origin header is not present terminate this set of steps. The request is
                // outside the scope of this specification.
                let has_origin = req.headers.get::<Origin>().is_some();
                if !has_origin {
                    return handler.handle(req);
                }
                //
                // 2.If the value of the Origin header is not a case-sensitive match for any of the
                // values in list of origins, do not set any additional headers and terminate this
                // set of steps.
                //
                // Note: Always matching is acceptable since the list of origins can be unbounded.
                //
                // 3. If the resource supports credentials add a single Access-Control-Allow-Origin
                // header, with the value of the Origin header as value, and add a single
                // Access-Control-Allow-Credentials header with the case-sensitive string "true" as
                // value.
                //
                // Otherwise, add a single Access-Control-Allow-Origin header, with either the
                // value of the Origin header or the string "*" as value.
                //
                // Note: The string "*" cannot be used for a resource that supports credentials.
                //
                // 4. If the list of exposed headers is not empty add one or more
                // Access-Control-Expose-Headers headers, with as values the header field names
                // given in the list of exposed headers.
                let result = handler.handle(req);
                match result {
                    Ok(mut res) => {
                        // And set CORS headers
                        let origin = req.headers.get::<Origin>().unwrap();
                        res.headers.set(AccessControlAllowOrigin::Value(format!("{}", origin)));
                        Ok(res)
                    }
                    _ => result
                }
            }
        })
    }
}

#[cfg(test)]
mod tests {
	extern crate router;
	extern crate hyper;
	use iron::Listening;
	use self::router::Router;
	use iron::prelude::*;
	use iron::status;
	use self::hyper::Client;
	use self::hyper::header::Headers;
	use std::io::Read;
    use iron::headers::{Origin,
					ContentType,
                    AccessControlRequestMethod,
                    AccessControlAllowOrigin,
                    AccessControlAllowHeaders,
                    AccessControlMaxAge,
                    AccessControlAllowMethods};
    use iron::middleware::{AroundMiddleware, Handler};
    use iron::method::Method;
    use super::CorsMiddleware;
    use std::str::FromStr;

	struct AutoServer {
		listening: Listening,
		port: u16
	}

	impl AutoServer {
		pub fn new() -> AutoServer {
			let mut router = Router::new();
			router.get("/a",
               |_: &mut Request| Ok(Response::with((status::ImATeapot, ""))),
               "get_a");
            let mut chain = Chain::new(router);
            chain.link_around(CorsMiddleware);
    		let l = Iron::new(chain).http(format!("localhost:0")).unwrap();
			let p = l.socket.port();
			AutoServer { listening: l, port: p }
		}
	}

	impl Drop for AutoServer {
		fn drop(&mut self) {
			// Workaround for https://github.com/hyperium/hyper/issues/338
			self.listening.close().unwrap();
		}
	}

    #[test]
    fn normal_request_possible() {
		let server = AutoServer::new();
		let client = Client::new();
		let res = client.get(&format!("http://127.0.0.1:{}/a", server.port)).send().unwrap();
		assert_eq!(res.status, status::ImATeapot);
	}

    #[test]
    fn preflight_without_origin_is_bad_request() {
		let server = AutoServer::new();
		let client = Client::new();
		let mut headers = Headers::new();
		headers.set(AccessControlRequestMethod(Method::Get));
		let mut res = client
				.request(Method::Options, &format!("http://127.0.0.1:{}/a", server.port))
				.headers(headers)
				.send().unwrap();
		assert_eq!(res.status, status::BadRequest);
        let mut payload = String::new();
        res.read_to_string(&mut payload).unwrap();
        assert_eq!(payload, "Preflight request without Origin header");
	}

    #[test]
    fn preflight_with_origin_accepts_same_origin() {
		let server = AutoServer::new();
		let client = Client::new();
		let mut headers = Headers::new();
		headers.set(AccessControlRequestMethod(Method::Get));
		headers.set(Origin::from_str("http://www.a.com:8080").unwrap());
		let mut res = client
				.request(Method::Options, &format!("http://127.0.0.1:{}/a", server.port))
				.headers(headers)
				.send().unwrap();
		assert_eq!(res.status, status::NoContent);
        let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
        assert_eq!(format!("{}", allow_origin) , "http://www.a.com:8080");
        let allow_headers = res.headers.get::<AccessControlAllowHeaders>().unwrap();
        assert_eq!(format!("{}", allow_headers) , "Content-Type, X-Requested-With");
        let allow_methods = res.headers.get::<AccessControlAllowMethods>().unwrap();
        assert_eq!(format!("{}", allow_methods) , "GET, PUT, POST");
        let max_age = res.headers.get::<AccessControlMaxAge>().unwrap();
        assert_eq!(max_age.0, 60*60u32);
	}

    #[test]
    fn normal_request_allows_origin() {
		let server = AutoServer::new();
		let client = Client::new();
		let mut headers = Headers::new();
		headers.set(Origin::from_str("http://www.a.com:8080").unwrap());
		let res = client
            .get(&format!("http://127.0.0.1:{}/a", server.port))
            .headers(headers)
            .send().unwrap();
		assert_eq!(res.status, status::ImATeapot);
        let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
        assert_eq!(format!("{}", allow_origin) , "http://www.a.com:8080");
        assert!(res.headers.get::<AccessControlAllowHeaders>().is_none());
        assert!(res.headers.get::<AccessControlAllowMethods>().is_none());
        assert!(res.headers.get::<AccessControlMaxAge>().is_none());
	}

    #[test]
    fn normal_request_without_origin_is_passthrough() {
		let server = AutoServer::new();
		let client = Client::new();
		let res = client
            .get(&format!("http://127.0.0.1:{}/a", server.port))
            .send().unwrap();
		assert_eq!(res.status, status::ImATeapot);
        assert!(res.headers.get::<AccessControlAllowOrigin>().is_none());
        assert!(res.headers.get::<AccessControlAllowHeaders>().is_none());
        assert!(res.headers.get::<AccessControlAllowMethods>().is_none());
        assert!(res.headers.get::<AccessControlMaxAge>().is_none());
	}

}
<commit_msg>remove warnings<commit_after>extern crate iron;
extern crate unicase;

use unicase::UniCase;
use iron::prelude::*;
use iron::method::Method;
use iron::status;
use iron::headers::{Origin,
                    AccessControlRequestMethod,
                    AccessControlAllowOrigin,
                    AccessControlAllowHeaders,
                    AccessControlMaxAge,
                    AccessControlAllowMethods};
use iron::middleware::{AroundMiddleware, Handler};

pub struct CorsMiddleware;
impl AroundMiddleware for CorsMiddleware {

    fn around(self, handler: Box<Handler>) -> Box<Handler> {
        Box::new(move | req: &mut Request | {
            let allowed_headers: Vec<unicase::UniCase<String>> = vec![
                    // To allow application/json
                    UniCase("Content-Type".to_owned()),
                    // Set by some js libs
                    UniCase("X-Requested-With".to_owned()),
                    ];
            let allowed_methods: Vec<Method> = vec![ Method::Get, Method::Put, Method::Post ];
            if req.method == Method::Options &&
                req.headers.get::<AccessControlRequestMethod>().is_some() {
                // Preflight request
                // 1.If the Origin header is not present terminate this set of steps. The request is
                // outside the scope of this specification.
                let maybe_origin = req.headers.get::<Origin>();
                if maybe_origin.is_none() {
                    let resp = Response::with((status::BadRequest,
                                               "Preflight request without Origin header"));
                    return Ok(resp);
                }
                let origin = maybe_origin.unwrap();
                let mut res = Response::with((status::NoContent));
                //
                // 2.If the value of the Origin header is not a case-sensitive match for any of the
                // values in list of origins do not set any additional headers and terminate this
                // set of steps.
                //
                // Note: Always matching is acceptable since the list of origins can be unbounded.
                //
                // Note: The Origin header can only contain a single origin as the user agent
                //       will not follow redirects.
                //
                // 3. Let method be the value as result of parsing the Access-Control-Request-Method
                // header.
                //
                // If there is no Access-Control-Request-Method header or if parsing failed, do not
                // set any additional headers and terminate this set of steps. The request is
                // outside the scope of this specification.
                //
                // 4. Let header field-names be the values as result of parsing the
                // Access-Control-Request-Headers headers.
                //
                // If there are no Access-Control-Request-Headers headers let header field-names be
                // the empty list.
                //
                // If parsing failed do not set any additional headers and terminate this set of
                // steps. The request is outside the scope of this specification.
                //
                // 5.If method is not a case-sensitive match for any of the values in list of
                //   methods do not set any additional headers and terminate this set of steps.
                //
                // Always matching is acceptable since the list of methods can be unbounded.
                //
                // 6. If any of the header field-names is not a ASCII case-insensitive match for any
                // of the values in list of headers do not set any additional headers and terminate
                // this set of steps.
                //
                // Always matching is acceptable since the list of headers can be unbounded.
                //
                // 7. If the resource supports credentials add a single Access-Control-Allow-Origin
                // header, with the value of the Origin header as value, and add a single
                // Access-Control-Allow-Credentials header with the case-sensitive string "true" as
                // value.
                //
                // Otherwise, add a single Access-Control-Allow-Origin header, with either the
                // value of the Origin header or the string "*" as value.
                res.headers.set(AccessControlAllowOrigin::Value(format!("{}", origin)));
                //
                // The string "*" cannot be used for a resource that supports credentials.
                //
                // 8. Optionally add a single Access-Control-Max-Age header with as value the amount
                // of seconds the user agent is allowed to cache the result of the request.
                res.headers.set(AccessControlMaxAge(60*60));
                //
                // 9. If method is a simple method this step may be skipped.
                //
                // Add one or more Access-Control-Allow-Methods headers consisting of (a subset of)
                // the list of methods.
                res.headers.set(AccessControlAllowMethods(allowed_methods));
                //
                // If a method is a simple method it does not need to be listed, but this is not
                // prohibited.
                //
                // Since the list of methods can be unbounded, simply returning the method
                // indicated by Access-Control-Request-Method (if supported) can be enough.
                //
                // 10.If each of the header field-names is a simple header and none is Content-Type,
                // this step may be skipped.
                //
                // Add one or more Access-Control-Allow-Headers headers consisting of (a subset of)
                // the list of headers.
                res.headers.set(AccessControlAllowHeaders(allowed_headers));
                //
                // If a header field name is a simple header and is not Content-Type, it is not
                // required to be listed. Content-Type is to be listed as only a subset of its
                // values makes it qualify as simple header.
                //
                // Since the list of headers can be unbounded, simply returning supported headers
                // from Access-Control-Allow-Headers can be enough.
                Ok(res)
            } else {
                // Normal request
                // 1.If the Origin header is not present terminate this set of steps. The request is
                // outside the scope of this specification.
                let has_origin = req.headers.get::<Origin>().is_some();
                if !has_origin {
                    return handler.handle(req);
                }
                //
                // 2.If the value of the Origin header is not a case-sensitive match for any of the
                // values in list of origins, do not set any additional headers and terminate this
                // set of steps.
                //
                // Note: Always matching is acceptable since the list of origins can be unbounded.
                //
                // 3. If the resource supports credentials add a single Access-Control-Allow-Origin
                // header, with the value of the Origin header as value, and add a single
                // Access-Control-Allow-Credentials header with the case-sensitive string "true" as
                // value.
                //
                // Otherwise, add a single Access-Control-Allow-Origin header, with either the
                // value of the Origin header or the string "*" as value.
                //
                // Note: The string "*" cannot be used for a resource that supports credentials.
                //
                // 4. If the list of exposed headers is not empty add one or more
                // Access-Control-Expose-Headers headers, with as values the header field names
                // given in the list of exposed headers.
                let result = handler.handle(req);
                match result {
                    Ok(mut res) => {
                        // And set CORS headers
                        let origin = req.headers.get::<Origin>().unwrap();
                        res.headers.set(AccessControlAllowOrigin::Value(format!("{}", origin)));
                        Ok(res)
                    }
                    _ => result
                }
            }
        })
    }
}

#[cfg(test)]
mod tests {
	extern crate router;
	extern crate hyper;
	use iron::Listening;
	use self::router::Router;
	use iron::prelude::*;
	use iron::status;
	use self::hyper::Client;
	use self::hyper::header::Headers;
	use std::io::Read;
    use iron::headers::{Origin,
                    AccessControlRequestMethod,
                    AccessControlAllowOrigin,
                    AccessControlAllowHeaders,
                    AccessControlMaxAge,
                    AccessControlAllowMethods};
    use iron::method::Method;
    use super::CorsMiddleware;
    use std::str::FromStr;

	struct AutoServer {
		listening: Listening,
		port: u16
	}

	impl AutoServer {
		pub fn new() -> AutoServer {
			let mut router = Router::new();
            let handler = |_: &mut Request| Ok(Response::with((status::ImATeapot, "")));
			router.get("/a", handler,  "get_a");
            let mut chain = Chain::new(router);
            chain.link_around(CorsMiddleware);
    		let l = Iron::new(chain).http(format!("localhost:0")).unwrap();
			let p = l.socket.port();
			AutoServer { listening: l, port: p }
		}
	}

	impl Drop for AutoServer {
		fn drop(&mut self) {
			// Workaround for https://github.com/hyperium/hyper/issues/338
			self.listening.close().unwrap();
		}
	}

    #[test]
    fn normal_request_possible() {
		let server = AutoServer::new();
		let client = Client::new();
		let res = client.get(&format!("http://127.0.0.1:{}/a", server.port)).send().unwrap();
		assert_eq!(res.status, status::ImATeapot);
	}

    #[test]
    fn preflight_without_origin_is_bad_request() {
		let server = AutoServer::new();
		let client = Client::new();
		let mut headers = Headers::new();
		headers.set(AccessControlRequestMethod(Method::Get));
		let mut res = client
				.request(Method::Options, &format!("http://127.0.0.1:{}/a", server.port))
				.headers(headers)
				.send().unwrap();
		assert_eq!(res.status, status::BadRequest);
        let mut payload = String::new();
        res.read_to_string(&mut payload).unwrap();
        assert_eq!(payload, "Preflight request without Origin header");
	}

    #[test]
    fn preflight_with_origin_accepts_same_origin() {
		let server = AutoServer::new();
		let client = Client::new();
		let mut headers = Headers::new();
		headers.set(AccessControlRequestMethod(Method::Get));
		headers.set(Origin::from_str("http://www.a.com:8080").unwrap());
		let res = client
				.request(Method::Options, &format!("http://127.0.0.1:{}/a", server.port))
				.headers(headers)
				.send().unwrap();
		assert_eq!(res.status, status::NoContent);
        let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
        assert_eq!(format!("{}", allow_origin) , "http://www.a.com:8080");
        let allow_headers = res.headers.get::<AccessControlAllowHeaders>().unwrap();
        assert_eq!(format!("{}", allow_headers) , "Content-Type, X-Requested-With");
        let allow_methods = res.headers.get::<AccessControlAllowMethods>().unwrap();
        assert_eq!(format!("{}", allow_methods) , "GET, PUT, POST");
        let max_age = res.headers.get::<AccessControlMaxAge>().unwrap();
        assert_eq!(max_age.0, 60*60u32);
	}

    #[test]
    fn normal_request_allows_origin() {
		let server = AutoServer::new();
		let client = Client::new();
		let mut headers = Headers::new();
		headers.set(Origin::from_str("http://www.a.com:8080").unwrap());
		let res = client
            .get(&format!("http://127.0.0.1:{}/a", server.port))
            .headers(headers)
            .send().unwrap();
		assert_eq!(res.status, status::ImATeapot);
        let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
        assert_eq!(format!("{}", allow_origin) , "http://www.a.com:8080");
        assert!(res.headers.get::<AccessControlAllowHeaders>().is_none());
        assert!(res.headers.get::<AccessControlAllowMethods>().is_none());
        assert!(res.headers.get::<AccessControlMaxAge>().is_none());
	}

    #[test]
    fn normal_request_without_origin_is_passthrough() {
		let server = AutoServer::new();
		let client = Client::new();
		let res = client
            .get(&format!("http://127.0.0.1:{}/a", server.port))
            .send().unwrap();
		assert_eq!(res.status, status::ImATeapot);
        assert!(res.headers.get::<AccessControlAllowOrigin>().is_none());
        assert!(res.headers.get::<AccessControlAllowHeaders>().is_none());
        assert!(res.headers.get::<AccessControlAllowMethods>().is_none());
        assert!(res.headers.get::<AccessControlMaxAge>().is_none());
	}

}
<|endoftext|>