<commit_before>// ignore-windows: File handling is not implemented yet
// compile-flags: -Zmiri-disable-isolation

use std::fs::{File, remove_file, rename};
use std::io::{Read, Write, ErrorKind, Result, Seek, SeekFrom};
use std::path::{PathBuf, Path};

fn main() {
    test_file();
    test_file_clone();
    test_seek();
    test_metadata();
    test_symlink();
    test_errors();
    test_rename();
}

/// Prepare: compute filename and make sure the file does not exist.
fn prepare(filename: &str) -> PathBuf {
    let tmp = std::env::temp_dir();
    let path = tmp.join(filename);
    // Clean the paths for robustness.
    remove_file(&path).ok();
    path
}

fn test_file() {
    let path = prepare("miri_test_fs_file.txt");
    let bytes = b"Hello, World!\n";

    // Test creating, writing and closing a file (closing is tested when `file` is dropped).
    let mut file = File::create(&path).unwrap();
    // Writing 0 bytes should not change the file contents.
    file.write(&mut []).unwrap();
    assert_eq!(file.metadata().unwrap().len(), 0);

    file.write(bytes).unwrap();
    assert_eq!(file.metadata().unwrap().len(), bytes.len() as u64);
    // Test opening, reading and closing a file.
    let mut file = File::open(&path).unwrap();
    let mut contents = Vec::new();
    // Reading 0 bytes should not move the file pointer.
    file.read(&mut []).unwrap();
    // Reading until EOF should get the whole text.
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_file_clone() {
    let path = prepare("miri_test_fs_file_clone.txt");
    let bytes = b"Hello, World!\n";

    let mut file = File::create(&path).unwrap();
    file.write(bytes).unwrap();

    // Cloning a file should be successful.
    let file = File::open(&path).unwrap();
    let mut cloned = file.try_clone().unwrap();
    // Reading from a cloned file should get the same text.
    let mut contents = Vec::new();
    cloned.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_seek() {
    let path = prepare("miri_test_fs_seek.txt");
    let bytes = b"Hello, World!\n";

    let mut file = File::create(&path).unwrap();
    file.write(bytes).unwrap();

    let mut file = File::open(&path).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    // Test that seeking to the beginning and reading until EOF gets the text again.
    file.seek(SeekFrom::Start(0)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test seeking relative to the end of the file.
    file.seek(SeekFrom::End(-1)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(&bytes[bytes.len() - 1..], contents.as_slice());
    // Test seeking relative to the current position.
    file.seek(SeekFrom::Start(5)).unwrap();
    file.seek(SeekFrom::Current(-3)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(&bytes[2..], contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> {
    // Test that the file metadata is correct.
    let metadata = path.metadata()?;
    // `path` should point to a file.
    assert!(metadata.is_file());
    // The size of the file must be equal to the number of written bytes.
    assert_eq!(bytes.len() as u64, metadata.len());
    Ok(())
}

fn test_metadata() {
    let path = prepare("miri_test_fs_metadata.txt");
    let bytes = b"Hello, World!\n";

    let mut file = File::create(&path).unwrap();
    file.write(bytes).unwrap();

    // Test that metadata of an absolute path is correct.
    check_metadata(bytes, &path).unwrap();
    // Test that metadata of a relative path is correct.
    std::env::set_current_dir(path.parent().unwrap()).unwrap();
    check_metadata(bytes, Path::new(path.file_name().unwrap())).unwrap();

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_symlink() {
    let path = prepare("miri_test_fs_link_target.txt");
    let symlink_path = prepare("miri_test_fs_symlink.txt");
    let bytes = b"Hello, World!\n";

    let mut file = File::create(&path).unwrap();
    file.write(bytes).unwrap();

    // Creating a symbolic link should succeed.
    std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
    // Test that the symbolic link has the same contents as the file.
    let mut symlink_file = File::open(&symlink_path).unwrap();
    let mut contents = Vec::new();
    symlink_file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test that metadata of a symbolic link is correct.
    check_metadata(bytes, &symlink_path).unwrap();
    // Test that the metadata of a symbolic link is correct when not following it.
    assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink());
    // Removing symbolic link should succeed.
    remove_file(&symlink_path).unwrap();

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_errors() {
    let path = prepare("miri_test_fs_errors.txt");
    let bytes = b"Hello, World!\n";

    // The following tests also check that the `__errno_location()` shim is working properly.
    // Opening a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind());
    // Removing a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind());
    // Reading the metadata of a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, check_metadata(bytes, &path).unwrap_err().kind());
}

fn test_rename() {
    // Renaming a file should succeed.
    let path1 = prepare("miri_test_fs_rename_source.txt");
    let path2 = prepare("miri_test_fs_rename_destination.txt");

    let file = File::create(&path1).unwrap();
    drop(file);
    rename(&path1, &path2).unwrap();
    assert_eq!(ErrorKind::NotFound, path1.metadata().unwrap_err().kind());
    assert!(path2.metadata().unwrap().is_file());
    remove_file(&path2).unwrap();
}
<commit_msg>factor more common code<commit_after>// ignore-windows: File handling is not implemented yet
// compile-flags: -Zmiri-disable-isolation

use std::fs::{File, remove_file, rename};
use std::io::{Read, Write, ErrorKind, Result, Seek, SeekFrom};
use std::path::{PathBuf, Path};

fn main() {
    test_file();
    test_file_clone();
    test_seek();
    test_metadata();
    test_symlink();
    test_errors();
    test_rename();
}

/// Prepare: compute filename and make sure the file does not exist.
fn prepare(filename: &str) -> PathBuf {
    let tmp = std::env::temp_dir();
    let path = tmp.join(filename);
    // Clean the paths for robustness.
    remove_file(&path).ok();
    path
}

/// Prepare like above, and also write some initial content to the file.
fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
    let path = prepare(filename);
    let mut file = File::create(&path).unwrap();
    file.write(content).unwrap();
    path
}

fn test_file() {
    let bytes = b"Hello, World!\n";
    let path = prepare("miri_test_fs_file.txt");

    // Test creating, writing and closing a file (closing is tested when `file` is dropped).
    let mut file = File::create(&path).unwrap();
    // Writing 0 bytes should not change the file contents.
    file.write(&mut []).unwrap();
    assert_eq!(file.metadata().unwrap().len(), 0);

    file.write(bytes).unwrap();
    assert_eq!(file.metadata().unwrap().len(), bytes.len() as u64);
    // Test opening, reading and closing a file.
    let mut file = File::open(&path).unwrap();
    let mut contents = Vec::new();
    // Reading 0 bytes should not move the file pointer.
    file.read(&mut []).unwrap();
    // Reading until EOF should get the whole text.
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_file_clone() {
    let bytes = b"Hello, World!\n";
    let path = prepare_with_content("miri_test_fs_file_clone.txt", bytes);

    // Cloning a file should be successful.
    let file = File::open(&path).unwrap();
    let mut cloned = file.try_clone().unwrap();
    // Reading from a cloned file should get the same text.
    let mut contents = Vec::new();
    cloned.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_seek() {
    let bytes = b"Hello, entire World!\n";
    let path = prepare_with_content("miri_test_fs_seek.txt", bytes);

    let mut file = File::open(&path).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    // Test that seeking to the beginning and reading until EOF gets the text again.
    file.seek(SeekFrom::Start(0)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test seeking relative to the end of the file.
    file.seek(SeekFrom::End(-1)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(&bytes[bytes.len() - 1..], contents.as_slice());
    // Test seeking relative to the current position.
    file.seek(SeekFrom::Start(5)).unwrap();
    file.seek(SeekFrom::Current(-3)).unwrap();
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).unwrap();
    assert_eq!(&bytes[2..], contents.as_slice());

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> {
    // Test that the file metadata is correct.
    let metadata = path.metadata()?;
    // `path` should point to a file.
    assert!(metadata.is_file());
    // The size of the file must be equal to the number of written bytes.
    assert_eq!(bytes.len() as u64, metadata.len());
    Ok(())
}

fn test_metadata() {
    let bytes = b"Hello, meta-World!\n";
    let path = prepare_with_content("miri_test_fs_metadata.txt", bytes);

    // Test that metadata of an absolute path is correct.
    check_metadata(bytes, &path).unwrap();
    // Test that metadata of a relative path is correct.
    std::env::set_current_dir(path.parent().unwrap()).unwrap();
    check_metadata(bytes, Path::new(path.file_name().unwrap())).unwrap();

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_symlink() {
    let bytes = b"Hello, World!\n";
    let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
    let symlink_path = prepare("miri_test_fs_symlink.txt");

    // Creating a symbolic link should succeed.
    std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
    // Test that the symbolic link has the same contents as the file.
    let mut symlink_file = File::open(&symlink_path).unwrap();
    let mut contents = Vec::new();
    symlink_file.read_to_end(&mut contents).unwrap();
    assert_eq!(bytes, contents.as_slice());
    // Test that metadata of a symbolic link is correct.
    check_metadata(bytes, &symlink_path).unwrap();
    // Test that the metadata of a symbolic link is correct when not following it.
    assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink());
    // Removing symbolic link should succeed.
    remove_file(&symlink_path).unwrap();

    // Removing file should succeed.
    remove_file(&path).unwrap();
}

fn test_errors() {
    let bytes = b"Hello, World!\n";
    let path = prepare("miri_test_fs_errors.txt");

    // The following tests also check that the `__errno_location()` shim is working properly.
    // Opening a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind());
    // Removing a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind());
    // Reading the metadata of a non-existing file should fail with a "not found" error.
    assert_eq!(ErrorKind::NotFound, check_metadata(bytes, &path).unwrap_err().kind());
}

fn test_rename() {
    // Renaming a file should succeed.
    let path1 = prepare("miri_test_fs_rename_source.txt");
    let path2 = prepare("miri_test_fs_rename_destination.txt");

    let file = File::create(&path1).unwrap();
    drop(file);
    rename(&path1, &path2).unwrap();
    assert_eq!(ErrorKind::NotFound, path1.metadata().unwrap_err().kind());
    assert!(path2.metadata().unwrap().is_file());
    remove_file(&path2).unwrap();
}
<|endoftext|>


<commit_before>/*!
Test supports module.

*/
use glutin;
use glium::{mod, DisplayBuild};

use std::os;

/// Builds a headless display for tests.
pub fn build_display() -> glium::Display {
    if os::getenv("HEADLESS_TESTS").is_some() {
        glutin::HeadlessRendererBuilder::new(1024, 768).build_glium().unwrap()
    } else {
        glutin::WindowBuilder::new().with_visibility(false).build_glium().unwrap()
    }
}

/// Builds a 2x2 unicolor texture.
pub fn build_unicolor_texture2d(display: &glium::Display, red: f32, green: f32, blue: f32)
    -> glium::Texture2D
{
    let color = (red, green, blue);

    glium::texture::Texture2D::new(display, vec![
        vec![color, color],
        vec![color, color],
    ])
}
<commit_msg>Fix unsupported texture type in blitting test<commit_after>/*!
Test supports module.

*/
use glutin;
use glium::{mod, DisplayBuild};

use std::os;

/// Builds a headless display for tests.
pub fn build_display() -> glium::Display {
    if os::getenv("HEADLESS_TESTS").is_some() {
        glutin::HeadlessRendererBuilder::new(1024, 768).build_glium().unwrap()
    } else {
        glutin::WindowBuilder::new().with_visibility(false).build_glium().unwrap()
    }
}

/// Builds a 2x2 unicolor texture.
pub fn build_unicolor_texture2d(display: &glium::Display, red: f32, green: f32, blue: f32)
    -> glium::Texture2D
{
    let color = ((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8);

    glium::texture::Texture2D::new(display, vec![
        vec![color, color],
        vec![color, color],
    ])
}
<|endoftext|>


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

//! Roughtime client pseudo-Node functionality.

use crate::{
    io::{Receiver, ReceiverExt},
    node::invocation::InvocationExt,
    proto::oak::invocation::GrpcInvocation as Invocation,
    time::{
        get_default_servers, RoughtimeClient, RoughtimeServer, DEFAULT_MAX_RADIUS_MICROSECONDS,
        DEFAULT_MIN_OVERLAPPING_INTERVALS, DEFAULT_SERVER_RETRIES, DEFAULT_TIMEOUT_SECONDS,
    },
    RuntimeProxy,
};

use log::{debug, error, info};
use oak_abi::{proto::oak::application::RoughtimeClientConfiguration, OakStatus};
use oak_io::{handle::ReadHandle, OakError};
use oak_services::proto::{
    google::rpc::Code,
    oak::{
        encap::GrpcResponse,
        roughtime::{GetRoughtimeRequest, Roughtime},
    },
};
use prost::Message;
use tokio::sync::oneshot;

/// Roughtime client pseudo-Node.
pub struct RoughtimeClientNode {
    node_name: String,
    client: RoughtimeClient,
}

impl RoughtimeClientNode {
    /// Creates a new [`RoughtimeClientNode`] instance, but does not start it.
    pub fn new(node_name: &str, config: &RoughtimeClientConfiguration) -> Self {
        let timeout_seconds = config
            .timeout_seconds
            .map_or(DEFAULT_TIMEOUT_SECONDS, |value| value as u64);
        let server_retries = config
            .server_retries
            .map_or(DEFAULT_SERVER_RETRIES, |value| value as usize);
        let min_overlapping_intervals = config
            .min_overlapping_intervals
            .map_or(DEFAULT_MIN_OVERLAPPING_INTERVALS, |value| value as usize);
        let max_radius_microseconds = config
            .max_radius_microseconds
            .unwrap_or(DEFAULT_MAX_RADIUS_MICROSECONDS);
        let servers = if config.servers.is_empty() {
            get_default_servers()
        } else {
            config
                .servers
                .iter()
                .map(|server| {
                    RoughtimeServer::new(
                        &server.name,
                        &server.host,
                        server.port as u16,
                        &server.public_key_base64,
                    )
                })
                .collect()
        };

        Self {
            node_name: node_name.to_string(),
            client: RoughtimeClient::new(
                servers,
                min_overlapping_intervals,
                max_radius_microseconds,
                timeout_seconds,
                server_retries,
            ),
        }
    }

    /// Processes a gRPC invocation.
    fn process_invocation(&self, runtime: &RuntimeProxy, invocation: &Invocation) {
        match invocation.receive_request(runtime) {
            Ok(request) => {
                // TODO(#1113): Generate this code automatically.
                if request.method_name != "/oak.roughtime.RoughtimeService/GetRoughtime" {
                    let message = format!("Unknown method_name: {}", request.method_name);
                    invocation.send_error(Code::NotFound, &message, runtime);
                }
                if GetRoughtimeRequest::decode(request.req_msg.as_slice()).is_err() {
                    invocation.send_error(
                        Code::InvalidArgument,
                        "Could not parse request.",
                        runtime,
                    );
                }
            }
            Err(error) => {
                let message = format!("Error reading request: {:?}", error);
                invocation.send_error(Code::InvalidArgument, &message, runtime);
            }
        };

        match self.client.get_roughtime() {
            Ok(time) => {
                let response = Roughtime {
                    roughtime_usec: time,
                };
                let mut message = Vec::new();
                match response.encode(&mut message) {
                    Ok(_) => {
                        let grpc_response = GrpcResponse {
                            rsp_msg: message,
                            status: None,
                            last: true,
                        };
                        let _ = invocation
                            .send_response(grpc_response, runtime)
                            .map_err(|error| {
                                error!("Couldn't send the response: {:?}", error);
                            });
                    }
                    Err(error) => {
                        let message = format!("Error encoding response: {:?}", error);
                        invocation.send_error(Code::Internal, &message, runtime);
                    }
                }
            }
            Err(error) => {
                let message = format!("Error getting Roughtime: {:?}", error);
                invocation.send_error(Code::Internal, &message, runtime);
            }
        };
    }
}

impl super::Node for RoughtimeClientNode {
    fn node_type(&self) -> &'static str {
        "roughtime-client"
    }

    /// Runs the node.
    fn run(
        self: Box<Self>,
        runtime: RuntimeProxy,
        handle: oak_abi::Handle,
        _notify_receiver: oneshot::Receiver<()>,
    ) {
        info!("{}: Starting Roughtime pseudo-Node", self.node_name);
        // Create a [`Receiver`] used for reading gRPC invocations.
        let receiver = Receiver::<Invocation>::new(ReadHandle { handle });
        loop {
            debug!("Waiting for gRPC invocation");
            // Read a gRPC invocation from the [`Receiver`].
            match receiver.receive(&runtime) {
                Ok(invocation) => {
                    self.process_invocation(&runtime, &invocation);
                    invocation.close(&runtime);
                }
                Err(OakError::OakStatus(OakStatus::ErrTerminated)) => {
                    break;
                }
                Err(error) => {
                    error!("Couldn't receive the invocation: {:?}", error);
                    break;
                }
            }
        }
    }
}
<commit_msg>runtime: tweak Roughtime error paths<commit_after>//
// Copyright 2020 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Roughtime client pseudo-Node functionality.

use crate::{
    io::{Receiver, ReceiverExt},
    node::invocation::InvocationExt,
    proto::oak::invocation::GrpcInvocation as Invocation,
    time::{
        get_default_servers, RoughtimeClient, RoughtimeServer, DEFAULT_MAX_RADIUS_MICROSECONDS,
        DEFAULT_MIN_OVERLAPPING_INTERVALS, DEFAULT_SERVER_RETRIES, DEFAULT_TIMEOUT_SECONDS,
    },
    RuntimeProxy,
};

use log::{debug, error, info};
use oak_abi::{proto::oak::application::RoughtimeClientConfiguration, OakStatus};
use oak_io::{handle::ReadHandle, OakError};
use oak_services::proto::{
    google::rpc::Code,
    oak::{
        encap::GrpcResponse,
        roughtime::{GetRoughtimeRequest, Roughtime},
    },
};
use prost::Message;
use tokio::sync::oneshot;

/// Roughtime client pseudo-Node.
pub struct RoughtimeClientNode {
    node_name: String,
    client: RoughtimeClient,
}

impl RoughtimeClientNode {
    /// Creates a new [`RoughtimeClientNode`] instance, but does not start it.
    pub fn new(node_name: &str, config: &RoughtimeClientConfiguration) -> Self {
        let timeout_seconds = config
            .timeout_seconds
            .map_or(DEFAULT_TIMEOUT_SECONDS, |value| value as u64);
        let server_retries = config
            .server_retries
            .map_or(DEFAULT_SERVER_RETRIES, |value| value as usize);
        let min_overlapping_intervals = config
            .min_overlapping_intervals
            .map_or(DEFAULT_MIN_OVERLAPPING_INTERVALS, |value| value as usize);
        let max_radius_microseconds = config
            .max_radius_microseconds
            .unwrap_or(DEFAULT_MAX_RADIUS_MICROSECONDS);
        let servers = if config.servers.is_empty() {
            get_default_servers()
        } else {
            config
                .servers
                .iter()
                .map(|server| {
                    RoughtimeServer::new(
                        &server.name,
                        &server.host,
                        server.port as u16,
                        &server.public_key_base64,
                    )
                })
                .collect()
        };

        Self {
            node_name: node_name.to_string(),
            client: RoughtimeClient::new(
                servers,
                min_overlapping_intervals,
                max_radius_microseconds,
                timeout_seconds,
                server_retries,
            ),
        }
    }

    /// Processes a gRPC invocation.
    fn process_invocation(&self, runtime: &RuntimeProxy, invocation: &Invocation) {
        match invocation.receive_request(runtime) {
            Ok(request) => {
                // TODO(#1113): Generate this code automatically.
                if request.method_name != "/oak.roughtime.RoughtimeService/GetRoughtime" {
                    let message = format!("Unknown method_name: {}", request.method_name);
                    invocation.send_error(Code::NotFound, &message, runtime);
                }
                if GetRoughtimeRequest::decode(request.req_msg.as_slice()).is_err() {
                    invocation.send_error(
                        Code::InvalidArgument,
                        "Could not parse request.",
                        runtime,
                    );
                    return;
                }
            }
            Err(error) => {
                let message = format!("Error reading request: {:?}", error);
                invocation.send_error(Code::InvalidArgument, &message, runtime);
                return;
            }
        };

        match self.client.get_roughtime() {
            Ok(time) => {
                let response = Roughtime {
                    roughtime_usec: time,
                };
                let mut message = Vec::new();
                match response.encode(&mut message) {
                    Ok(_) => {
                        let grpc_response = GrpcResponse {
                            rsp_msg: message,
                            status: None,
                            last: true,
                        };
                        let _ = invocation
                            .send_response(grpc_response, runtime)
                            .map_err(|error| {
                                error!("Couldn't send the response: {:?}", error);
                            });
                    }
                    Err(error) => {
                        let message = format!("Error encoding response: {:?}", error);
                        invocation.send_error(Code::Internal, &message, runtime);
                    }
                }
            }
            Err(error) => {
                let message = format!("Error getting Roughtime: {:?}", error);
                invocation.send_error(Code::Internal, &message, runtime);
            }
        };
    }
}

impl super::Node for RoughtimeClientNode {
    fn node_type(&self) -> &'static str {
        "roughtime-client"
    }

    /// Runs the node.
    fn run(
        self: Box<Self>,
        runtime: RuntimeProxy,
        handle: oak_abi::Handle,
        _notify_receiver: oneshot::Receiver<()>,
    ) {
        info!("{}: Starting Roughtime pseudo-Node", self.node_name);
        // Create a [`Receiver`] used for reading gRPC invocations.
        let receiver = Receiver::<Invocation>::new(ReadHandle { handle });
        loop {
            debug!("Waiting for gRPC invocation");
            // Read a gRPC invocation from the [`Receiver`].
            match receiver.receive(&runtime) {
                Ok(invocation) => {
                    self.process_invocation(&runtime, &invocation);
                    invocation.close(&runtime);
                }
                Err(OakError::OakStatus(OakStatus::ErrTerminated)) => {
                    break;
                }
                Err(error) => {
                    error!("Couldn't receive the invocation: {:?}", error);
                    break;
                }
            }
        }
        let _ = runtime.channel_close(handle);
        info!(
            "{} Roughtime pseudo-Node execution complete",
            self.node_name
        );
    }
}
<|endoftext|>


<commit_before>#![doc(hidden)]

macro_rules! doc_comment {
    ($x:expr, $($tt:tt)*) => {
        #[doc = $x]
        $($tt)*
    };
}

macro_rules! int_module {
    ($T:ident) => (int_module!($T, #[stable(feature = "rust1", since = "1.0.0")]););
    ($T:ident, #[$attr:meta]) => (
        doc_comment! {
            concat!("The smallest value that can be represented by this integer type.
Use [`", stringify!($T), "::MIN", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MIN) instead."),
            #[$attr]
            pub const MIN: $T = $T::min_value();
        }

        doc_comment! {
            concat!("The largest value that can be represented by this integer type.
Use [`", stringify!($T), "::MAX", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MAX) instead."),
            #[$attr]
            pub const MAX: $T = $T::max_value();
        }
    )
}
<commit_msg>Define module level int consts from assoc consts<commit_after>#![doc(hidden)]

macro_rules! doc_comment {
    ($x:expr, $($tt:tt)*) => {
        #[doc = $x]
        $($tt)*
    };
}

macro_rules! int_module {
    ($T:ident) => (int_module!($T, #[stable(feature = "rust1", since = "1.0.0")]););
    ($T:ident, #[$attr:meta]) => (
        doc_comment! {
            concat!("The smallest value that can be represented by this integer type.
Use [`", stringify!($T), "::MIN", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MIN) instead."),
            #[$attr]
            pub const MIN: $T = $T::MIN;
        }

        doc_comment! {
            concat!("The largest value that can be represented by this integer type.
Use [`", stringify!($T), "::MAX", "`](../../std/primitive.", stringify!($T), ".html#associatedconstant.MAX) instead."),
            #[$attr]
            pub const MAX: $T = $T::MAX;
        }
    )
}
<|endoftext|>


<commit_before>use super::methods::{HelloMessage, RPCMethod, RPCRequest, RPCResponse};
use libp2p::core::{upgrade, InboundUpgrade, OutboundUpgrade, UpgradeInfo};
use ssz::{ssz_encode, Decodable, Encodable, SszStream};
use std::io;
use std::iter;
use tokio::io::{AsyncRead, AsyncWrite};

/// The maximum bytes that can be sent across the RPC.
const MAX_READ_SIZE: usize = 4_194_304; // 4M

/// Implementation of the `ConnectionUpgrade` for the rpc protocol.

#[derive(Debug, Clone)]
pub struct RPCProtocol;

impl UpgradeInfo for RPCProtocol {
    type Info = &'static [u8];
    type InfoIter = iter::Once<Self::Info>;

    #[inline]
    fn protocol_info(&self) -> Self::InfoIter {
        iter::once(b"/eth/serenity/rpc/1.0.0")
    }
}

impl Default for RPCProtocol {
    fn default() -> Self {
        RPCProtocol
    }
}

/// The RPC types which are sent/received in this protocol.
#[derive(Debug, Clone)]
pub enum RPCEvent {
    Request {
        id: u64,
        method_id: u16,
        body: RPCRequest,
    },
    Response {
        id: u64,
        method_id: u16, //TODO: Remove and process decoding upstream
        result: RPCResponse,
    },
}

impl UpgradeInfo for RPCEvent {
    type Info = &'static [u8];
    type InfoIter = iter::Once<Self::Info>;

    #[inline]
    fn protocol_info(&self) -> Self::InfoIter {
        iter::once(b"/eth/serenity/rpc/1.0.0")
    }
}

impl<TSocket> InboundUpgrade<TSocket> for RPCProtocol
where
    TSocket: AsyncRead + AsyncWrite,
{
    type Output = RPCEvent;
    type Error = DecodeError;
    type Future =
        upgrade::ReadOneThen<TSocket, (), fn(Vec<u8>, ()) -> Result<RPCEvent, DecodeError>>;

    fn upgrade_inbound(self, socket: TSocket, _: Self::Info) -> Self::Future {
        upgrade::read_one_then(socket, MAX_READ_SIZE, (), |packet, ()| Ok(decode(packet)?))
    }
}

fn decode(packet: Vec<u8>) -> Result<RPCEvent, DecodeError> {
    // decode the header of the rpc
    // request/response
    let (request, index) = bool::ssz_decode(&packet, 0)?;
    let (id, index) = u64::ssz_decode(&packet, index)?;
    let (method_id, index) = u16::ssz_decode(&packet, index)?;

    if request {
        let body = match RPCMethod::from(method_id) {
            RPCMethod::Hello => {
                let (hello_body, _index) = HelloMessage::ssz_decode(&packet, index)?;
                RPCRequest::Hello(hello_body)
            }
            RPCMethod::Unknown | _ => return Err(DecodeError::UnknownRPCMethod),
        };

        Ok(RPCEvent::Request {
            id,
            method_id,
            body,
        })
    }
    // we have received a response
    else {
        let result = match RPCMethod::from(method_id) {
            RPCMethod::Hello => {
                let (body, _index) = HelloMessage::ssz_decode(&packet, index)?;
                RPCResponse::Hello(body)
            }
            RPCMethod::Unknown | _ => return Err(DecodeError::UnknownRPCMethod),
        };
        Ok(RPCEvent::Response {
            id,
            method_id,
            result,
        })
    }
}

impl<TSocket> OutboundUpgrade<TSocket> for RPCEvent
where
    TSocket: AsyncWrite,
{
    type Output = ();
    type Error = io::Error;
    type Future = upgrade::WriteOne<TSocket>;

    #[inline]
    fn upgrade_outbound(self, socket: TSocket, _: Self::Info) -> Self::Future {
        let bytes = ssz_encode(&self);
        upgrade::write_one(socket, bytes)
    }
}

impl Encodable for RPCEvent {
    fn ssz_append(&self, s: &mut SszStream) {
        match self {
            RPCEvent::Request {
                id,
                method_id,
                body,
            } => {
                s.append(&true);
                s.append(id);
                s.append(method_id);
                match body {
                    RPCRequest::Hello(body) => {
                        s.append(body);
                    }
                    _ => {}
                }
            }
            RPCEvent::Response {
                id,
                method_id,
                result,
            } => {
                s.append(&false);
                s.append(id);
                s.append(method_id);
                match result {
                    RPCResponse::Hello(response) => {
                        s.append(response);
                    }
                    _ => {}
                }
            }
        }
    }
}

#[derive(Debug)]
pub enum DecodeError {
    ReadError(upgrade::ReadOneError),
    SSZDecodeError(ssz::DecodeError),
    UnknownRPCMethod,
}

impl From<upgrade::ReadOneError> for DecodeError {
    #[inline]
    fn from(err: upgrade::ReadOneError) -> Self {
        DecodeError::ReadError(err)
    }
}

impl From<ssz::DecodeError> for DecodeError {
    #[inline]
    fn from(err: ssz::DecodeError) -> Self {
        DecodeError::SSZDecodeError(err)
    }
}
<commit_msg>Implement Goodbye RPC call<commit_after>use super::methods::{HelloMessage, RPCMethod, RPCRequest, RPCResponse};
use libp2p::core::{upgrade, InboundUpgrade, OutboundUpgrade, UpgradeInfo};
use ssz::{ssz_encode, Decodable, Encodable, SszStream};
use std::io;
use std::iter;
use tokio::io::{AsyncRead, AsyncWrite};

/// The maximum bytes that can be sent across the RPC.
const MAX_READ_SIZE: usize = 4_194_304; // 4M

/// Implementation of the `ConnectionUpgrade` for the rpc protocol.

#[derive(Debug, Clone)]
pub struct RPCProtocol;

impl UpgradeInfo for RPCProtocol {
    type Info = &'static [u8];
    type InfoIter = iter::Once<Self::Info>;

    #[inline]
    fn protocol_info(&self) -> Self::InfoIter {
        iter::once(b"/eth/serenity/rpc/1.0.0")
    }
}

impl Default for RPCProtocol {
    fn default() -> Self {
        RPCProtocol
    }
}

/// The RPC types which are sent/received in this protocol.
#[derive(Debug, Clone)]
pub enum RPCEvent {
    Request {
        id: u64,
        method_id: u16,
        body: RPCRequest,
    },
    Response {
        id: u64,
        method_id: u16, //TODO: Remove and process decoding upstream
        result: RPCResponse,
    },
}

impl UpgradeInfo for RPCEvent {
    type Info = &'static [u8];
    type InfoIter = iter::Once<Self::Info>;

    #[inline]
    fn protocol_info(&self) -> Self::InfoIter {
        iter::once(b"/eth/serenity/rpc/1.0.0")
    }
}

impl<TSocket> InboundUpgrade<TSocket> for RPCProtocol
where
    TSocket: AsyncRead + AsyncWrite,
{
    type Output = RPCEvent;
    type Error = DecodeError;
    type Future =
        upgrade::ReadOneThen<TSocket, (), fn(Vec<u8>, ()) -> Result<RPCEvent, DecodeError>>;

    fn upgrade_inbound(self, socket: TSocket, _: Self::Info) -> Self::Future {
        upgrade::read_one_then(socket, MAX_READ_SIZE, (), |packet, ()| Ok(decode(packet)?))
    }
}

fn decode(packet: Vec<u8>) -> Result<RPCEvent, DecodeError> {
    // decode the header of the rpc
    // request/response
    let (request, index) = bool::ssz_decode(&packet, 0)?;
    let (id, index) = u64::ssz_decode(&packet, index)?;
    let (method_id, index) = u16::ssz_decode(&packet, index)?;

    if request {
        let body = match RPCMethod::from(method_id) {
            RPCMethod::Hello => {
                let (hello_body, _index) = HelloMessage::ssz_decode(&packet, index)?;
                RPCRequest::Hello(hello_body)
            }
            RPCMethod::Goodbye => {
                let (goodbye_code, _index) = u64::ssz_decode(&packet, index)?;
                RPCRequest::Goodbye(goodbye_code)
            }
            RPCMethod::Unknown | _ => return Err(DecodeError::UnknownRPCMethod),
        };

        Ok(RPCEvent::Request {
            id,
            method_id,
            body,
        })
    }
    // we have received a response
    else {
        let result = match RPCMethod::from(method_id) {
            RPCMethod::Hello => {
                let (body, _index) = HelloMessage::ssz_decode(&packet, index)?;
                RPCResponse::Hello(body)
            }
            RPCMethod::Unknown | _ => return Err(DecodeError::UnknownRPCMethod),
        };
        Ok(RPCEvent::Response {
            id,
            method_id,
            result,
        })
    }
}

impl<TSocket> OutboundUpgrade<TSocket> for RPCEvent
where
    TSocket: AsyncWrite,
{
    type Output = ();
    type Error = io::Error;
    type Future = upgrade::WriteOne<TSocket>;

    #[inline]
    fn upgrade_outbound(self, socket: TSocket, _: Self::Info) -> Self::Future {
        let bytes = ssz_encode(&self);
        upgrade::write_one(socket, bytes)
    }
}

impl Encodable for RPCEvent {
    fn ssz_append(&self, s: &mut SszStream) {
        match self {
            RPCEvent::Request {
                id,
                method_id,
                body,
            } => {
                s.append(&true);
                s.append(id);
                s.append(method_id);
                match body {
                    RPCRequest::Hello(body) => {
                        s.append(body);
                    }
                    _ => {}
                }
            }
            RPCEvent::Response {
                id,
                method_id,
                result,
            } => {
                s.append(&false);
                s.append(id);
                s.append(method_id);
                match result {
                    RPCResponse::Hello(response) => {
                        s.append(response);
                    }
                    _ => {}
                }
            }
        }
    }
}

#[derive(Debug)]
pub enum DecodeError {
    ReadError(upgrade::ReadOneError),
    SSZDecodeError(ssz::DecodeError),
    UnknownRPCMethod,
}

impl From<upgrade::ReadOneError> for DecodeError {
    #[inline]
    fn from(err: upgrade::ReadOneError) -> Self {
        DecodeError::ReadError(err)
    }
}

impl From<ssz::DecodeError> for DecodeError {
    #[inline]
    fn from(err: ssz::DecodeError) -> Self {
        DecodeError::SSZDecodeError(err)
    }
}
<|endoftext|>


<commit_before>//! `bless` updates the 'expected output' files in the repo with changed output files
//! from the last test run.

use std::env;
use std::ffi::OsStr;
use std::fs;
use std::lazy::SyncLazy;
use std::path::PathBuf;
use walkdir::WalkDir;

use crate::clippy_project_root;

// NOTE: this is duplicated with tests/cargo/mod.rs What to do?
pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
    Some(v) => v.into(),
    None => env::current_dir().unwrap().join("target"),
});

pub fn bless() {
    let test_dirs = [
        clippy_project_root().join("tests").join("ui"),
        clippy_project_root().join("tests").join("ui-toml"),
        clippy_project_root().join("tests").join("ui-cargo"),
    ];
    for test_dir in &test_dirs {
        WalkDir::new(test_dir)
            .into_iter()
            .filter_map(Result::ok)
            .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
            .for_each(|f| {
                update_test_file(f.path().with_extension("stdout"));
                update_test_file(f.path().with_extension("stderr"));
                update_test_file(f.path().with_extension("fixed"));
            });
    }
}

fn update_test_file(test_file_path: PathBuf) {
    let build_output_path = build_dir().join(PathBuf::from(test_file_path.file_name().unwrap()));
    let relative_test_file_path = test_file_path.strip_prefix(clippy_project_root()).unwrap();

    // If compiletest did not write any changes during the test run,
    // we don't have to update anything
    if !build_output_path.exists() {
        return;
    }

    let build_output = fs::read(&build_output_path).expect("Unable to read build output file");
    let test_file = fs::read(&test_file_path).expect("Unable to read test file");

    if build_output != test_file {
        // If a test run caused an output file to change, update the test file
        println!("updating {}", &relative_test_file_path.display());
        fs::copy(build_output_path, &test_file_path).expect("Could not update test file");

        if test_file.is_empty() {
            // If we copied over an empty output file, we remove it
            println!("removing {}", &relative_test_file_path.display());
            fs::remove_file(test_file_path).expect("Could not remove test file");
        }
    }
}

fn build_dir() -> PathBuf {
    let profile = format!("{}", env::var("PROFILE").unwrap_or("debug".to_string()));
    let mut path = PathBuf::new();
    path.push(CARGO_TARGET_DIR.clone());
    path.push(profile);
    path.push("test_build_base");
    path
}
<commit_msg>Improve variable naming<commit_after>//! `bless` updates the reference files in the repo with changed output files
//! from the last test run.

use std::env;
use std::ffi::OsStr;
use std::fs;
use std::lazy::SyncLazy;
use std::path::PathBuf;
use walkdir::WalkDir;

use crate::clippy_project_root;

// NOTE: this is duplicated with tests/cargo/mod.rs What to do?
pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
    Some(v) => v.into(),
    None => env::current_dir().unwrap().join("target"),
});

pub fn bless() {
    let test_dirs = [
        clippy_project_root().join("tests").join("ui"),
        clippy_project_root().join("tests").join("ui-toml"),
        clippy_project_root().join("tests").join("ui-cargo"),
    ];
    for test_dir in &test_dirs {
        WalkDir::new(test_dir)
            .into_iter()
            .filter_map(Result::ok)
            .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
            .for_each(|f| {
                update_reference_file(f.path().with_extension("stdout"));
                update_reference_file(f.path().with_extension("stderr"));
                update_reference_file(f.path().with_extension("fixed"));
            });
    }
}

fn update_reference_file(reference_file_path: PathBuf) {
    let test_output_path = build_dir().join(PathBuf::from(reference_file_path.file_name().unwrap()));
    let relative_reference_file_path = reference_file_path.strip_prefix(clippy_project_root()).unwrap();

    // If compiletest did not write any changes during the test run,
    // we don't have to update anything
    if !test_output_path.exists() {
        return;
    }

    let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
    let reference_file = fs::read(&reference_file_path).expect("Unable to read reference file");

    if test_output_file != reference_file {
        // If a test run caused an output file to change, update the reference file
        println!("updating {}", &relative_reference_file_path.display());
        fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file");

        if reference_file.is_empty() {
            // If we copied over an empty output file, we remove the now empty reference file
            println!("removing {}", &relative_reference_file_path.display());
            fs::remove_file(reference_file_path).expect("Could not remove reference file");
        }
    }
}

fn build_dir() -> PathBuf {
    let profile = format!("{}", env::var("PROFILE").unwrap_or("debug".to_string()));
    let mut path = PathBuf::new();
    path.push(CARGO_TARGET_DIR.clone());
    path.push(profile);
    path.push("test_build_base");
    path
}
<|endoftext|>


<commit_before>//! Log all requests in a format similar to Heroku's router, but with additional
//! information that we care about like User-Agent

use super::prelude::*;
use crate::util::request_header;

use conduit::{header, Host, RequestExt, Scheme, StatusCode};
use conduit_cookie::RequestSession;
use sentry::Level;

use std::fmt::{self, Display, Formatter};
use std::time::{SystemTime, UNIX_EPOCH};

const SLOW_REQUEST_THRESHOLD_MS: u64 = 1000;

const FILTERED_HEADERS: &[&str] = &["Authorization", "Cookie", "X-Real-Ip", "X-Forwarded-For"];

#[derive(Default)]
pub(super) struct LogRequests();

struct OriginalPath(String);

impl Middleware for LogRequests {
    fn before(&self, req: &mut dyn RequestExt) -> BeforeResult {
        let path = OriginalPath(req.path().to_string());
        req.mut_extensions().insert(path);
        Ok(())
    }

    fn after(&self, req: &mut dyn RequestExt, res: AfterResult) -> AfterResult {
        let response_time =
            if let Ok(start_ms) = request_header(req, "x-request-start").parse::<u128>() {
                let current_ms = SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .expect("Time went way backwards")
                    .as_millis();

                if current_ms > start_ms {
                    // The result cannot be negative
                    current_ms - start_ms
                } else {
                    // Because our nginx proxy and app run on the same dyno in production, we
                    // shouldn't have to worry about clock drift. But if something goes wrong,
                    // calculate the response time based on when the request reached this app.
                    fallback_response_time(req)
                }
            } else {
                // X-Request-Start header couldn't be parsed.
                // We are probably running locally and not behind nginx.
                fallback_response_time(req)
            };

        // This will only trucate for requests lasting > 500 million years
        let response_time = response_time as u64;

        println!(
            "{}",
            RequestLine {
                req,
                res: &res,
                response_time,
            }
        );

        report_to_sentry(req, &res, response_time);

        res
    }
}

/// Calculate the response time based on when the request reached the in-app web server.
///
/// This serves as a fallback in case the `X-Request-Start` header is missing or invalid.
fn fallback_response_time(req: &mut dyn RequestExt) -> u128 {
    req.elapsed().as_millis()
}

struct CustomMetadata {
    entries: Vec<(&'static str, String)>,
}

pub fn add_custom_metadata<V: Display>(req: &mut dyn RequestExt, key: &'static str, value: V) {
    if let Some(metadata) = req.mut_extensions().find_mut::<CustomMetadata>() {
        metadata.entries.push((key, value.to_string()));
    } else {
        let mut metadata = CustomMetadata {
            entries: Vec::new(),
        };
        metadata.entries.push((key, value.to_string()));
        req.mut_extensions().insert(metadata);
    }
}

fn report_to_sentry(req: &dyn RequestExt, res: &AfterResult, response_time: u64) {
    let (message, level) = match res {
        Err(e) => (e.to_string(), Level::Error),
        Ok(_) => return,
    };

    let config = |scope: &mut sentry::Scope| {
        let method = Some(req.method().as_str().to_owned());

        let scheme = match req.scheme() {
            Scheme::Http => "http",
            Scheme::Https => "https",
        };

        let host = match req.host() {
            Host::Name(name) => name.to_owned(),
            Host::Socket(addr) => addr.to_string(),
        };

        let path = &req.extensions().find::<OriginalPath>().unwrap().0;

        let url = format!("{}://{}{}", scheme, host, path).parse().ok();

        {
            let id = req.session().get("user_id").map(|str| str.to_string());

            let user = sentry::User {
                id,
                ..Default::default()
            };

            scope.set_user(Some(user));
        }

        {
            let headers = req
                .headers()
                .iter()
                .filter(|(k, _v)| !FILTERED_HEADERS.iter().any(|name| k == name))
                .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or_default().to_string()))
                .collect();

            let sentry_req = sentry::protocol::Request {
                url,
                method,
                headers,
                ..Default::default()
            };

            scope.add_event_processor(Box::new(move |mut event| {
                if event.request.is_none() {
                    event.request = Some(sentry_req.clone());
                }
                Some(event)
            }));
        }

        if let Some(request_id) = req
            .headers()
            .get("x-request-id")
            .and_then(|value| value.to_str().ok())
        {
            scope.set_tag("request.id", request_id);
        }

        {
            let status = res
                .as_ref()
                .map(|resp| resp.status())
                .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);

            scope.set_tag("response.status", status.as_str());
        }

        scope.set_extra("Response time [ms]", response_time.into());

        if let Some(metadata) = req.extensions().find::<CustomMetadata>() {
            for (key, value) in &metadata.entries {
                scope.set_extra(key, value.to_string().into());
            }
        }
    };

    sentry::with_scope(config, || sentry::capture_message(&message, level));
}

#[cfg(test)]
pub(crate) fn get_log_message(req: &dyn RequestExt, key: &'static str) -> String {
    // Unwrap shouldn't panic as no other code has access to the private struct to remove it
    for (k, v) in &req.extensions().find::<CustomMetadata>().unwrap().entries {
        if key == *k {
            return v.clone();
        }
    }
    panic!("expected log message for {} not found", key);
}

struct RequestLine<'r> {
    req: &'r dyn RequestExt,
    res: &'r AfterResult,
    response_time: u64,
}

impl Display for RequestLine<'_> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        let mut line = LogLine::new(f);

        let (at, status) = match self.res {
            Ok(resp) => ("info", resp.status()),
            Err(_) => ("error", StatusCode::INTERNAL_SERVER_ERROR),
        };

        line.add_field("at", at)?;
        line.add_field("method", self.req.method())?;
        line.add_quoted_field("path", FullPath(self.req))?;
        line.add_field("request_id", request_header(self.req, "x-request-id"))?;
        line.add_quoted_field("fwd", request_header(self.req, "x-real-ip"))?;
        line.add_field("service", TimeMs(self.response_time))?;
        line.add_field("status", status.as_str())?;
        line.add_quoted_field("user_agent", request_header(self.req, header::USER_AGENT))?;

        if let Some(metadata) = self.req.extensions().find::<CustomMetadata>() {
            for (key, value) in &metadata.entries {
                line.add_quoted_field(key, value)?;
            }
        }

        if let Err(err) = self.res {
            line.add_quoted_field("error", err)?;
        }

        if self.response_time > SLOW_REQUEST_THRESHOLD_MS {
            line.add_marker("SLOW REQUEST")?;
        }

        Ok(())
    }
}

struct FullPath<'a>(&'a dyn RequestExt);

impl<'a> Display for FullPath<'a> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        // Unwrap shouldn't panic as no other code has access to the private struct to remove it
        write!(
            f,
            "{}",
            self.0.extensions().find::<OriginalPath>().unwrap().0
        )?;
        if let Some(q_string) = self.0.query_string() {
            write!(f, "?{}", q_string)?;
        }
        Ok(())
    }
}

struct TimeMs(u64);

impl Display for TimeMs {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)?;
        f.write_str("ms")?;
        Ok(())
    }
}

struct LogLine<'f, 'g> {
    f: &'f mut Formatter<'g>,
    first: bool,
}

impl<'f, 'g> LogLine<'f, 'g> {
    fn new(f: &'f mut Formatter<'g>) -> Self {
        Self { f, first: true }
    }

    fn add_field<K: Display, V: Display>(&mut self, key: K, value: V) -> fmt::Result {
        self.start_item()?;

        key.fmt(self.f)?;
        self.f.write_str("=")?;
        value.fmt(self.f)?;

        Ok(())
    }

    fn add_quoted_field<K: Display, V: Display>(&mut self, key: K, value: V) -> fmt::Result {
        self.start_item()?;

        key.fmt(self.f)?;
        self.f.write_str("=\"")?;
        value.fmt(self.f)?;
        self.f.write_str("\"")?;

        Ok(())
    }

    fn add_marker<M: Display>(&mut self, marker: M) -> fmt::Result {
        self.start_item()?;

        marker.fmt(self.f)?;

        Ok(())
    }

    fn start_item(&mut self) -> fmt::Result {
        if !self.first {
            self.f.write_str(" ")?;
        }
        self.first = false;
        Ok(())
    }
}
<commit_msg>Auto merge of #3793 - jtgeibel:reduce-log-output, r=pietroalbini<commit_after>//! Log all requests in a format similar to Heroku's router, but with additional
//! information that we care about like User-Agent

use super::prelude::*;
use crate::util::request_header;

use conduit::{header, Host, RequestExt, Scheme, StatusCode};
use conduit_cookie::RequestSession;
use sentry::Level;

use std::fmt::{self, Display, Formatter};
use std::time::{SystemTime, UNIX_EPOCH};

const SLOW_REQUEST_THRESHOLD_MS: u64 = 1000;

const FILTERED_HEADERS: &[&str] = &["Authorization", "Cookie", "X-Real-Ip", "X-Forwarded-For"];

#[derive(Default)]
pub(super) struct LogRequests();

struct OriginalPath(String);

impl Middleware for LogRequests {
    fn before(&self, req: &mut dyn RequestExt) -> BeforeResult {
        let path = OriginalPath(req.path().to_string());
        req.mut_extensions().insert(path);
        Ok(())
    }

    fn after(&self, req: &mut dyn RequestExt, res: AfterResult) -> AfterResult {
        let response_time =
            if let Ok(start_ms) = request_header(req, "x-request-start").parse::<u128>() {
                let current_ms = SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .expect("Time went way backwards")
                    .as_millis();

                if current_ms > start_ms {
                    // The result cannot be negative
                    current_ms - start_ms
                } else {
                    // Because our nginx proxy and app run on the same dyno in production, we
                    // shouldn't have to worry about clock drift. But if something goes wrong,
                    // calculate the response time based on when the request reached this app.
                    fallback_response_time(req)
                }
            } else {
                // X-Request-Start header couldn't be parsed.
                // We are probably running locally and not behind nginx.
                fallback_response_time(req)
            };

        // This will only trucate for requests lasting > 500 million years
        let response_time = response_time as u64;

        println!(
            "{}",
            RequestLine {
                req,
                res: &res,
                response_time,
            }
        );

        report_to_sentry(req, &res, response_time);

        res
    }
}

/// Calculate the response time based on when the request reached the in-app web server.
///
/// This serves as a fallback in case the `X-Request-Start` header is missing or invalid.
fn fallback_response_time(req: &mut dyn RequestExt) -> u128 {
    req.elapsed().as_millis()
}

struct CustomMetadata {
    entries: Vec<(&'static str, String)>,
}

pub fn add_custom_metadata<V: Display>(req: &mut dyn RequestExt, key: &'static str, value: V) {
    if let Some(metadata) = req.mut_extensions().find_mut::<CustomMetadata>() {
        metadata.entries.push((key, value.to_string()));
    } else {
        let mut metadata = CustomMetadata {
            entries: Vec::new(),
        };
        metadata.entries.push((key, value.to_string()));
        req.mut_extensions().insert(metadata);
    }
}

fn report_to_sentry(req: &dyn RequestExt, res: &AfterResult, response_time: u64) {
    let (message, level) = match res {
        Err(e) => (e.to_string(), Level::Error),
        Ok(_) => return,
    };

    let config = |scope: &mut sentry::Scope| {
        let method = Some(req.method().as_str().to_owned());

        let scheme = match req.scheme() {
            Scheme::Http => "http",
            Scheme::Https => "https",
        };

        let host = match req.host() {
            Host::Name(name) => name.to_owned(),
            Host::Socket(addr) => addr.to_string(),
        };

        let path = &req.extensions().find::<OriginalPath>().unwrap().0;

        let url = format!("{}://{}{}", scheme, host, path).parse().ok();

        {
            let id = req.session().get("user_id").map(|str| str.to_string());

            let user = sentry::User {
                id,
                ..Default::default()
            };

            scope.set_user(Some(user));
        }

        {
            let headers = req
                .headers()
                .iter()
                .filter(|(k, _v)| !FILTERED_HEADERS.iter().any(|name| k == name))
                .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or_default().to_string()))
                .collect();

            let sentry_req = sentry::protocol::Request {
                url,
                method,
                headers,
                ..Default::default()
            };

            scope.add_event_processor(Box::new(move |mut event| {
                if event.request.is_none() {
                    event.request = Some(sentry_req.clone());
                }
                Some(event)
            }));
        }

        if let Some(request_id) = req
            .headers()
            .get("x-request-id")
            .and_then(|value| value.to_str().ok())
        {
            scope.set_tag("request.id", request_id);
        }

        {
            let status = res
                .as_ref()
                .map(|resp| resp.status())
                .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);

            scope.set_tag("response.status", status.as_str());
        }

        scope.set_extra("Response time [ms]", response_time.into());

        if let Some(metadata) = req.extensions().find::<CustomMetadata>() {
            for (key, value) in &metadata.entries {
                scope.set_extra(key, value.to_string().into());
            }
        }
    };

    sentry::with_scope(config, || sentry::capture_message(&message, level));
}

#[cfg(test)]
pub(crate) fn get_log_message(req: &dyn RequestExt, key: &'static str) -> String {
    // Unwrap shouldn't panic as no other code has access to the private struct to remove it
    for (k, v) in &req.extensions().find::<CustomMetadata>().unwrap().entries {
        if key == *k {
            return v.clone();
        }
    }
    panic!("expected log message for {} not found", key);
}

struct RequestLine<'r> {
    req: &'r dyn RequestExt,
    res: &'r AfterResult,
    response_time: u64,
}

impl Display for RequestLine<'_> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        let mut line = LogLine::new(f);

        let (at, status) = match self.res {
            Ok(resp) => ("info", resp.status()),
            Err(_) => ("error", StatusCode::INTERNAL_SERVER_ERROR),
        };

        line.add_field("at", at)?;
        line.add_field("method", self.req.method())?;
        line.add_quoted_field("path", FullPath(self.req))?;

        // The request_id is not logged for successful download requests
        if !(self.req.path().ends_with("/download")
            && self
                .res
                .as_ref()
                .ok()
                .map(|ok| ok.status().is_redirection())
                == Some(true))
        {
            line.add_field("request_id", request_header(self.req, "x-request-id"))?;
        }

        line.add_quoted_field("fwd", request_header(self.req, "x-real-ip"))?;
        line.add_field("service", TimeMs(self.response_time))?;
        line.add_field("status", status.as_str())?;
        line.add_quoted_field("user_agent", request_header(self.req, header::USER_AGENT))?;

        if let Some(metadata) = self.req.extensions().find::<CustomMetadata>() {
            for (key, value) in &metadata.entries {
                line.add_quoted_field(key, value)?;
            }
        }

        if let Err(err) = self.res {
            line.add_quoted_field("error", err)?;
        }

        if self.response_time > SLOW_REQUEST_THRESHOLD_MS {
            line.add_marker("SLOW REQUEST")?;
        }

        Ok(())
    }
}

struct FullPath<'a>(&'a dyn RequestExt);

impl<'a> Display for FullPath<'a> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        // Unwrap shouldn't panic as no other code has access to the private struct to remove it
        write!(
            f,
            "{}",
            self.0.extensions().find::<OriginalPath>().unwrap().0
        )?;
        if let Some(q_string) = self.0.query_string() {
            write!(f, "?{}", q_string)?;
        }
        Ok(())
    }
}

struct TimeMs(u64);

impl Display for TimeMs {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)?;
        f.write_str("ms")?;
        Ok(())
    }
}

struct LogLine<'f, 'g> {
    f: &'f mut Formatter<'g>,
    first: bool,
}

impl<'f, 'g> LogLine<'f, 'g> {
    fn new(f: &'f mut Formatter<'g>) -> Self {
        Self { f, first: true }
    }

    fn add_field<K: Display, V: Display>(&mut self, key: K, value: V) -> fmt::Result {
        self.start_item()?;

        key.fmt(self.f)?;
        self.f.write_str("=")?;
        value.fmt(self.f)?;

        Ok(())
    }

    fn add_quoted_field<K: Display, V: Display>(&mut self, key: K, value: V) -> fmt::Result {
        self.start_item()?;

        key.fmt(self.f)?;
        self.f.write_str("=\"")?;
        value.fmt(self.f)?;
        self.f.write_str("\"")?;

        Ok(())
    }

    fn add_marker<M: Display>(&mut self, marker: M) -> fmt::Result {
        self.start_item()?;

        marker.fmt(self.f)?;

        Ok(())
    }

    fn start_item(&mut self) -> fmt::Result {
        if !self.first {
            self.f.write_str(" ")?;
        }
        self.first = false;
        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Filesystem syscalls
use core::sync::atomic::Ordering;

use context;
use scheme;
use syscall::data::Packet;
use syscall::error::*;

pub fn file_op(a: usize, fd: usize, c: usize, d: usize) -> Result<usize> {
    let (file, pid, uid, gid) = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
        (file, context.id, context.euid, context.egid)
    };

    let scheme = {
        let schemes = scheme::schemes();
        let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
        scheme.clone()
    };

    let mut packet = Packet {
        id: 0,
        pid: pid,
        uid: uid,
        gid: gid,
        a: a,
        b: file.number,
        c: c,
        d: d
    };

    scheme.handle(&mut packet);

    Error::demux(packet.a)
}

pub fn file_op_slice(a: usize, fd: usize, slice: &[u8]) -> Result<usize> {
    file_op(a, fd, slice.as_ptr() as usize, slice.len())
}

pub fn file_op_mut_slice(a: usize, fd: usize, slice: &mut [u8]) -> Result<usize> {
    file_op(a, fd, slice.as_mut_ptr() as usize, slice.len())
}

/// Change the current working directory
pub fn chdir(path: &[u8]) -> Result<usize> {
    let contexts = context::contexts();
    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
    let context = context_lock.read();
    let canonical = context.canonicalize(path);
    *context.cwd.lock() = canonical;
    Ok(0)
}

/// Get the current working directory
pub fn getcwd(buf: &mut [u8]) -> Result<usize> {
    let contexts = context::contexts();
    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
    let context = context_lock.read();
    let cwd = context.cwd.lock();
    let mut i = 0;
    while i < buf.len() && i < cwd.len() {
        buf[i] = cwd[i];
        i += 1;
    }
    Ok(i)
}

/// Open syscall
pub fn open(path: &[u8], flags: usize) -> Result<usize> {
    let (path_canon, uid, gid) = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        (context.canonicalize(path), context.euid, context.egid)
    };

    let mut parts = path_canon.splitn(2, |&b| b == b':');
    let namespace_opt = parts.next();
    let reference_opt = parts.next();

    let (scheme_id, file_id) = {
        let namespace = namespace_opt.ok_or(Error::new(ENODEV))?;
        let (scheme_id, scheme) = {
            let schemes = scheme::schemes();
            let (scheme_id, scheme) = schemes.get_name(namespace).ok_or(Error::new(ENODEV))?;
            (scheme_id, scheme.clone())
        };
        let file_id = scheme.open(reference_opt.unwrap_or(b""), flags, uid, gid)?;
        (scheme_id, file_id)
    };

    let contexts = context::contexts();
    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
    let context = context_lock.read();
    context.add_file(::context::file::File {
        scheme: scheme_id,
        number: file_id,
        event: None,
    }).ok_or(Error::new(EMFILE))
}

pub fn pipe2(fds: &mut [usize], flags: usize) -> Result<usize> {
    if fds.len() >= 2 {
        let scheme_id = ::scheme::pipe::PIPE_SCHEME_ID.load(Ordering::SeqCst);
        let (read_id, write_id) = ::scheme::pipe::pipe(flags);

        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();

        let read_fd = context.add_file(::context::file::File {
            scheme: scheme_id,
            number: read_id,
            event: None,
        }).ok_or(Error::new(EMFILE))?;

        let write_fd = context.add_file(::context::file::File {
            scheme: scheme_id,
            number: write_id,
            event: None,
        }).ok_or(Error::new(EMFILE))?;

        fds[0] = read_fd;
        fds[1] = write_fd;

        Ok(0)
    } else {
        Err(Error::new(EFAULT))
    }
}

/// mkdir syscall
pub fn mkdir(path: &[u8], mode: u16) -> Result<usize> {
    let (path_canon, uid, gid) = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        (context.canonicalize(path), context.euid, context.egid)
    };

    let mut parts = path_canon.splitn(2, |&b| b == b':');
    let namespace_opt = parts.next();
    let reference_opt = parts.next();

    let namespace = namespace_opt.ok_or(Error::new(ENODEV))?;
    let scheme = {
        let schemes = scheme::schemes();
        let (_scheme_id, scheme) = schemes.get_name(namespace).ok_or(Error::new(ENODEV))?;
        scheme.clone()
    };
    scheme.mkdir(reference_opt.unwrap_or(b""), mode, uid, gid)
}

/// rmdir syscall
pub fn rmdir(path: &[u8]) -> Result<usize> {
    let (path_canon, uid, gid) = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        (context.canonicalize(path), context.euid, context.egid)
    };

    let mut parts = path_canon.splitn(2, |&b| b == b':');
    let namespace_opt = parts.next();
    let reference_opt = parts.next();

    let namespace = namespace_opt.ok_or(Error::new(ENODEV))?;
    let scheme = {
        let schemes = scheme::schemes();
        let (_scheme_id, scheme) = schemes.get_name(namespace).ok_or(Error::new(ENODEV))?;
        scheme.clone()
    };
    scheme.rmdir(reference_opt.unwrap_or(b""), uid, gid)
}

/// Unlink syscall
pub fn unlink(path: &[u8]) -> Result<usize> {
    let (path_canon, uid, gid) = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        (context.canonicalize(path), context.euid, context.egid)
    };

    let mut parts = path_canon.splitn(2, |&b| b == b':');
    let namespace_opt = parts.next();
    let reference_opt = parts.next();

    let namespace = namespace_opt.ok_or(Error::new(ENODEV))?;
    let scheme = {
        let schemes = scheme::schemes();
        let (_scheme_id, scheme) = schemes.get_name(namespace).ok_or(Error::new(ENODEV))?;
        scheme.clone()
    };
    scheme.unlink(reference_opt.unwrap_or(b""), uid, gid)
}

/// Close syscall
pub fn close(fd: usize) -> Result<usize> {
    let file = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        let file = context.remove_file(fd).ok_or(Error::new(EBADF))?;
        file
    };

    if let Some(event_id) = file.event {
        context::event::unregister(fd, file.scheme, event_id);
    }

    let scheme = {
        let schemes = scheme::schemes();
        let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
        scheme.clone()
    };
    scheme.close(file.number)
}

/// Duplicate file descriptor
pub fn dup(fd: usize, buf: &[u8]) -> Result<usize> {
    let file = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
        file
    };

    let new_id = {
        let scheme = {
            let schemes = scheme::schemes();
            let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
            scheme.clone()
        };
        scheme.dup(file.number, buf)?
    };

    let contexts = context::contexts();
    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
    let context = context_lock.read();
    context.add_file(::context::file::File {
        scheme: file.scheme,
        number: new_id,
        event: None,
    }).ok_or(Error::new(EMFILE))
}

/// Register events for file
pub fn fevent(fd: usize, flags: usize) -> Result<usize> {
    let file = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        let mut files = context.files.lock();
        let mut file = files.get_mut(fd).ok_or(Error::new(EBADF))?.ok_or(Error::new(EBADF))?;
        if let Some(event_id) = file.event.take() {
            println!("{}: {}:{}: events already registered: {}", fd, file.scheme, file.number, event_id);
            context::event::unregister(fd, file.scheme, event_id);
        }
        file.clone()
    };

    let scheme = {
        let schemes = scheme::schemes();
        let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
        scheme.clone()
    };
    let event_id = scheme.fevent(file.number, flags)?;
    {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        let mut files = context.files.lock();
        let mut file = files.get_mut(fd).ok_or(Error::new(EBADF))?.ok_or(Error::new(EBADF))?;
        file.event = Some(event_id);
    }
    context::event::register(fd, file.scheme, event_id);
    Ok(0)
}
<commit_msg>Fix #725 by checking mode in chdir<commit_after>//! Filesystem syscalls
use core::sync::atomic::Ordering;

use context;
use scheme;
use syscall;
use syscall::data::{Packet, Stat};
use syscall::error::*;
use syscall::flag::{MODE_DIR, MODE_FILE};

pub fn file_op(a: usize, fd: usize, c: usize, d: usize) -> Result<usize> {
    let (file, pid, uid, gid) = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
        (file, context.id, context.euid, context.egid)
    };

    let scheme = {
        let schemes = scheme::schemes();
        let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
        scheme.clone()
    };

    let mut packet = Packet {
        id: 0,
        pid: pid,
        uid: uid,
        gid: gid,
        a: a,
        b: file.number,
        c: c,
        d: d
    };

    scheme.handle(&mut packet);

    Error::demux(packet.a)
}

pub fn file_op_slice(a: usize, fd: usize, slice: &[u8]) -> Result<usize> {
    file_op(a, fd, slice.as_ptr() as usize, slice.len())
}

pub fn file_op_mut_slice(a: usize, fd: usize, slice: &mut [u8]) -> Result<usize> {
    file_op(a, fd, slice.as_mut_ptr() as usize, slice.len())
}

/// Change the current working directory
pub fn chdir(path: &[u8]) -> Result<usize> {
    let fd = open(path, 0)?;
    let mut stat = Stat::default();
    let stat_res = file_op_mut_slice(syscall::number::SYS_FSTAT, fd, &mut stat);
    let _ = close(fd);
    stat_res?;
    if stat.st_mode & (MODE_FILE | MODE_DIR) == MODE_DIR {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        let canonical = context.canonicalize(path);
        *context.cwd.lock() = canonical;
        Ok(0)
    } else {
        Err(Error::new(ENOTDIR))
    }
}

/// Get the current working directory
pub fn getcwd(buf: &mut [u8]) -> Result<usize> {
    let contexts = context::contexts();
    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
    let context = context_lock.read();
    let cwd = context.cwd.lock();
    let mut i = 0;
    while i < buf.len() && i < cwd.len() {
        buf[i] = cwd[i];
        i += 1;
    }
    Ok(i)
}

/// Open syscall
pub fn open(path: &[u8], flags: usize) -> Result<usize> {
    let (path_canon, uid, gid) = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        (context.canonicalize(path), context.euid, context.egid)
    };

    let mut parts = path_canon.splitn(2, |&b| b == b':');
    let namespace_opt = parts.next();
    let reference_opt = parts.next();

    let (scheme_id, file_id) = {
        let namespace = namespace_opt.ok_or(Error::new(ENODEV))?;
        let (scheme_id, scheme) = {
            let schemes = scheme::schemes();
            let (scheme_id, scheme) = schemes.get_name(namespace).ok_or(Error::new(ENODEV))?;
            (scheme_id, scheme.clone())
        };
        let file_id = scheme.open(reference_opt.unwrap_or(b""), flags, uid, gid)?;
        (scheme_id, file_id)
    };

    let contexts = context::contexts();
    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
    let context = context_lock.read();
    context.add_file(::context::file::File {
        scheme: scheme_id,
        number: file_id,
        event: None,
    }).ok_or(Error::new(EMFILE))
}

pub fn pipe2(fds: &mut [usize], flags: usize) -> Result<usize> {
    if fds.len() >= 2 {
        let scheme_id = ::scheme::pipe::PIPE_SCHEME_ID.load(Ordering::SeqCst);
        let (read_id, write_id) = ::scheme::pipe::pipe(flags);

        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();

        let read_fd = context.add_file(::context::file::File {
            scheme: scheme_id,
            number: read_id,
            event: None,
        }).ok_or(Error::new(EMFILE))?;

        let write_fd = context.add_file(::context::file::File {
            scheme: scheme_id,
            number: write_id,
            event: None,
        }).ok_or(Error::new(EMFILE))?;

        fds[0] = read_fd;
        fds[1] = write_fd;

        Ok(0)
    } else {
        Err(Error::new(EFAULT))
    }
}

/// mkdir syscall
pub fn mkdir(path: &[u8], mode: u16) -> Result<usize> {
    let (path_canon, uid, gid) = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        (context.canonicalize(path), context.euid, context.egid)
    };

    let mut parts = path_canon.splitn(2, |&b| b == b':');
    let namespace_opt = parts.next();
    let reference_opt = parts.next();

    let namespace = namespace_opt.ok_or(Error::new(ENODEV))?;
    let scheme = {
        let schemes = scheme::schemes();
        let (_scheme_id, scheme) = schemes.get_name(namespace).ok_or(Error::new(ENODEV))?;
        scheme.clone()
    };
    scheme.mkdir(reference_opt.unwrap_or(b""), mode, uid, gid)
}

/// rmdir syscall
pub fn rmdir(path: &[u8]) -> Result<usize> {
    let (path_canon, uid, gid) = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        (context.canonicalize(path), context.euid, context.egid)
    };

    let mut parts = path_canon.splitn(2, |&b| b == b':');
    let namespace_opt = parts.next();
    let reference_opt = parts.next();

    let namespace = namespace_opt.ok_or(Error::new(ENODEV))?;
    let scheme = {
        let schemes = scheme::schemes();
        let (_scheme_id, scheme) = schemes.get_name(namespace).ok_or(Error::new(ENODEV))?;
        scheme.clone()
    };
    scheme.rmdir(reference_opt.unwrap_or(b""), uid, gid)
}

/// Unlink syscall
pub fn unlink(path: &[u8]) -> Result<usize> {
    let (path_canon, uid, gid) = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        (context.canonicalize(path), context.euid, context.egid)
    };

    let mut parts = path_canon.splitn(2, |&b| b == b':');
    let namespace_opt = parts.next();
    let reference_opt = parts.next();

    let namespace = namespace_opt.ok_or(Error::new(ENODEV))?;
    let scheme = {
        let schemes = scheme::schemes();
        let (_scheme_id, scheme) = schemes.get_name(namespace).ok_or(Error::new(ENODEV))?;
        scheme.clone()
    };
    scheme.unlink(reference_opt.unwrap_or(b""), uid, gid)
}

/// Close syscall
pub fn close(fd: usize) -> Result<usize> {
    let file = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        let file = context.remove_file(fd).ok_or(Error::new(EBADF))?;
        file
    };

    if let Some(event_id) = file.event {
        context::event::unregister(fd, file.scheme, event_id);
    }

    let scheme = {
        let schemes = scheme::schemes();
        let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
        scheme.clone()
    };
    scheme.close(file.number)
}

/// Duplicate file descriptor
pub fn dup(fd: usize, buf: &[u8]) -> Result<usize> {
    let file = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        let file = context.get_file(fd).ok_or(Error::new(EBADF))?;
        file
    };

    let new_id = {
        let scheme = {
            let schemes = scheme::schemes();
            let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
            scheme.clone()
        };
        scheme.dup(file.number, buf)?
    };

    let contexts = context::contexts();
    let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
    let context = context_lock.read();
    context.add_file(::context::file::File {
        scheme: file.scheme,
        number: new_id,
        event: None,
    }).ok_or(Error::new(EMFILE))
}

/// Register events for file
pub fn fevent(fd: usize, flags: usize) -> Result<usize> {
    let file = {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        let mut files = context.files.lock();
        let mut file = files.get_mut(fd).ok_or(Error::new(EBADF))?.ok_or(Error::new(EBADF))?;
        if let Some(event_id) = file.event.take() {
            println!("{}: {}:{}: events already registered: {}", fd, file.scheme, file.number, event_id);
            context::event::unregister(fd, file.scheme, event_id);
        }
        file.clone()
    };

    let scheme = {
        let schemes = scheme::schemes();
        let scheme = schemes.get(file.scheme).ok_or(Error::new(EBADF))?;
        scheme.clone()
    };
    let event_id = scheme.fevent(file.number, flags)?;
    {
        let contexts = context::contexts();
        let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
        let context = context_lock.read();
        let mut files = context.files.lock();
        let mut file = files.get_mut(fd).ok_or(Error::new(EBADF))?.ok_or(Error::new(EBADF))?;
        file.event = Some(event_id);
    }
    context::event::register(fd, file.scheme, event_id);
    Ok(0)
}
<|endoftext|>


<commit_before>use std::net::SocketAddr;
use std::str::FromStr;

use hyper::header;
use hyper::method::Method;

use url::Host;
use url::SchemeData;

use urlencoded;

use unicase::UniCase;

use iron;
use iron::prelude::*;

use models;

pub struct XForwardedMiddleware;

fn get_one_raw(r: &mut Request, key: &str) -> Option<String> {
    r.headers
        .get_raw(key)
        .and_then(|vals| if vals.len() > 0 { vals.to_owned().pop() } else { None })
        .and_then(|x| String::from_utf8(x).ok())
}

impl iron::middleware::BeforeMiddleware for XForwardedMiddleware {
    fn before(&self, r: &mut Request) -> IronResult<()> {
        get_one_raw(r, "X-Forwarded-Host")
            .and_then(|host| Host::parse(&host[..]).ok())
            .map(|x| r.url.host = x);

        get_one_raw(r, "X-Forwarded-Port")
            .and_then(|s| u16::from_str(&s[..]).ok())
            .map(|port| r.url.port = port);

        get_one_raw(r, "X-Forwarded-For")
            .and_then(parse_remote_addrs)
            .map(|ip| r.remote_addr = ip);

        get_one_raw(r, "X-Forwarded-Proto")
            .map(|scheme| r.url.scheme = scheme.to_lowercase());

        Ok(())
    }
}

fn parse_remote_addrs(s: String) -> Option<SocketAddr> {
    let split = s.split(',');
    let mut iter_ips = split.map(|x| x.trim()).filter(|x| x.len() > 0);
    iter_ips.next().and_then(|ip| SocketAddr::from_str(ip).ok())
}

pub struct SecurityHeaderMiddleware;

impl iron::middleware::AfterMiddleware for SecurityHeaderMiddleware {
    fn after(&self, request: &mut Request, mut response: Response) -> IronResult<Response> {
        set_cors_headers(&request, &mut response);
        set_frame_options(&request, &mut response);
        Ok(response)
    }

    fn catch(&self, request: &mut Request, mut error: IronError) -> IronResult<Response> {
        set_cors_headers(&request, &mut error.response);
        set_frame_options(&request, &mut error.response);
        Err(error)
    }
}

/// Required by remoteStorage spec
fn set_cors_headers(rq: &Request, r: &mut Response) {
    match &rq.url.path[0][..] {
        ".well-known" | "storage" => (),
        _ => return
    };

    let origin = match rq.headers.get_raw("Origin") {
        Some(x) => if x.len() == 1 {
            match String::from_utf8(x.to_owned().into_iter().next().unwrap()) {
                Ok(x) => x,
                Err(_) => return
            }
        } else {
            return;
        },
        None => return
    };

    r.headers.set(header::AccessControlAllowOrigin::Value(origin));
    r.headers.set(header::AccessControlExposeHeaders(vec![
        UniCase("ETag".to_owned()),
        UniCase("Content-Length".to_owned())
    ]));
    r.headers.set(header::AccessControlAllowMethods(vec![Method::Get, Method::Put, Method::Delete]));
    r.headers.set(header::AccessControlAllowHeaders(vec![
        UniCase("Authorization".to_owned()),
        UniCase("Content-Type".to_owned()),
        UniCase("Origin".to_owned()),
        UniCase("If-Match".to_owned()),
        UniCase("If-None-Match".to_owned()),
    ]));
}

/// Prevent clickjacking attacks like described in OAuth RFC
/// https://tools.ietf.org/html/rfc6749#section-10.13
fn set_frame_options(rq: &Request, r: &mut Response) {
    if &rq.url.path[0] == "storage" {
        // It's probably fine to embed user storage data into other documents
        return
    };

    r.headers.set_raw("X-Frame-Options", vec![b"DENY".to_vec()]);

    // This is a newer way to do what X-Frame-Options does
    // http://www.w3.org/TR/CSP11/#frame-ancestors-and-frame-options
    r.headers.set_raw("Content-Security-Policy", vec![b"frame-ancestors 'none'".to_vec()]);
}

pub trait EtagMatcher {
    fn matches_etag(&self, given: Option<&str>) -> bool;
}

impl EtagMatcher for header::IfNoneMatch {
    fn matches_etag(&self, given: Option<&str>) -> bool {
        match *self {
            header::IfNoneMatch::Any => given.is_some(),
            header::IfNoneMatch::Items(ref values) => {
                match given {
                    Some(given_value) => values.iter().any(|val| val.tag() == &given_value[..]),
                    None => false
                }
            }
        }
    }
}

impl EtagMatcher for header::IfMatch {
    fn matches_etag(&self, given: Option<&str>) -> bool {
        match *self {
            header::IfMatch::Any => given.is_some(),
            header::IfMatch::Items(ref values) => {
                match given {
                    Some(given_value) => values.iter().any(|val| val.tag() == &given_value[..]),
                    None => false
                }
            }
        }
    }
}

pub fn preconditions_ok(request: &Request, etag: Option<&str>) -> bool {
    match request.headers.get::<header::IfNoneMatch>() {
        Some(header) => if header.matches_etag(etag) { return false; },
        None => ()
    };

    match request.headers.get::<header::IfMatch>() {
        Some(header) => if !header.matches_etag(etag) { return false; },
        None => ()
    };
    true
}

pub trait FormDataHelper<K: ?Sized, V> {
    fn get_only<Q: AsRef<K>>(&self, k: Q) -> Option<&String>;
}

impl FormDataHelper<str, String> for urlencoded::QueryMap {
    fn get_only<Q: AsRef<str>>(&self, k: Q) -> Option<&String> {
        match self.get(&k.as_ref().to_owned()) {
            Some(x) if x.len() == 1 => Some(&x[0]),
            _ => None
        }
    }
}

pub fn get_account_id(user: &models::User, request: &Request) -> String {
    let url = request.url.clone().into_generic_url();
    let scheme_data = match url.scheme_data {
        SchemeData::Relative(ref x) => x,
        _ => panic!("Expected relative scheme data.")
    };

    let mut rv = format!("{}@{}", user.userid, scheme_data.host);
    if let Some(port) = scheme_data.port {
        if Some(port) != scheme_data.default_port {
            rv.push_str(&format!(":{}", port)[..]);
        }
    };

    rv
}
<commit_msg>Refactor security headers<commit_after>use std::net::SocketAddr;
use std::str::FromStr;

use hyper::header;
use hyper::method::Method;

use url::Host;
use url::SchemeData;

use urlencoded;

use unicase::UniCase;

use iron;
use iron::prelude::*;

use models;

pub struct XForwardedMiddleware;

fn get_one_raw(r: &mut Request, key: &str) -> Option<String> {
    r.headers
        .get_raw(key)
        .and_then(|vals| if vals.len() > 0 { vals.to_owned().pop() } else { None })
        .and_then(|x| String::from_utf8(x).ok())
}

impl iron::middleware::BeforeMiddleware for XForwardedMiddleware {
    fn before(&self, r: &mut Request) -> IronResult<()> {
        get_one_raw(r, "X-Forwarded-Host")
            .and_then(|host| Host::parse(&host[..]).ok())
            .map(|x| r.url.host = x);

        get_one_raw(r, "X-Forwarded-Port")
            .and_then(|s| u16::from_str(&s[..]).ok())
            .map(|port| r.url.port = port);

        get_one_raw(r, "X-Forwarded-For")
            .and_then(parse_remote_addrs)
            .map(|ip| r.remote_addr = ip);

        get_one_raw(r, "X-Forwarded-Proto")
            .map(|scheme| r.url.scheme = scheme.to_lowercase());

        Ok(())
    }
}

fn parse_remote_addrs(s: String) -> Option<SocketAddr> {
    let split = s.split(',');
    let mut iter_ips = split.map(|x| x.trim()).filter(|x| x.len() > 0);
    iter_ips.next().and_then(|ip| SocketAddr::from_str(ip).ok())
}

pub struct SecurityHeaderMiddleware;

impl iron::middleware::AfterMiddleware for SecurityHeaderMiddleware {
    fn after(&self, request: &mut Request, mut response: Response) -> IronResult<Response> {
        Self::set_security_headers(&request, &mut response);
        Ok(response)
    }

    fn catch(&self, request: &mut Request, mut error: IronError) -> IronResult<Response> {
        Self::set_security_headers(&request, &mut error.response);
        Err(error)
    }
}

impl SecurityHeaderMiddleware {
    fn set_security_headers(rq: &Request, r: &mut Response) {
        Self::set_cors_headers(rq, r);
        r.headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]);

        let mut csp = vec!["default-src 'self'"];

        if &rq.url.path[0] != "storage" {
            // It's probably fine to embed user storage data into other documents

            // Prevent clickjacking attacks like described in OAuth RFC
            // https://tools.ietf.org/html/rfc6749#section-10.13
            r.headers.set_raw("X-Frame-Options", vec![b"DENY".to_vec()]);

            // This is a newer way to do what X-Frame-Options does
            // http://www.w3.org/TR/CSP11/#frame-ancestors-and-frame-options
            csp.push("frame-ancestors 'none'");
        };
        r.headers.set_raw("Content-Security-Policy", vec![csp.join(";").as_bytes().to_vec()]);
    }

    /// Required by remoteStorage spec
    fn set_cors_headers(rq: &Request, r: &mut Response) {
        match &rq.url.path[0][..] {
            ".well-known" | "storage" => (),
            _ => return
        };

        let origin = match rq.headers.get_raw("Origin") {
            Some(x) => if x.len() == 1 {
                match String::from_utf8(x.to_owned().into_iter().next().unwrap()) {
                    Ok(x) => x,
                    Err(_) => return
                }
            } else {
                return;
            },
            None => return
        };

        r.headers.set(header::AccessControlAllowOrigin::Value(origin));
        r.headers.set(header::AccessControlExposeHeaders(vec![
            UniCase("ETag".to_owned()),
            UniCase("Content-Length".to_owned())
        ]));
        r.headers.set(header::AccessControlAllowMethods(vec![Method::Get, Method::Put, Method::Delete]));
        r.headers.set(header::AccessControlAllowHeaders(vec![
            UniCase("Authorization".to_owned()),
            UniCase("Content-Type".to_owned()),
            UniCase("Origin".to_owned()),
            UniCase("If-Match".to_owned()),
            UniCase("If-None-Match".to_owned()),
        ]));
    }
}

pub trait EtagMatcher {
    fn matches_etag(&self, given: Option<&str>) -> bool;
}

impl EtagMatcher for header::IfNoneMatch {
    fn matches_etag(&self, given: Option<&str>) -> bool {
        match *self {
            header::IfNoneMatch::Any => given.is_some(),
            header::IfNoneMatch::Items(ref values) => {
                match given {
                    Some(given_value) => values.iter().any(|val| val.tag() == &given_value[..]),
                    None => false
                }
            }
        }
    }
}

impl EtagMatcher for header::IfMatch {
    fn matches_etag(&self, given: Option<&str>) -> bool {
        match *self {
            header::IfMatch::Any => given.is_some(),
            header::IfMatch::Items(ref values) => {
                match given {
                    Some(given_value) => values.iter().any(|val| val.tag() == &given_value[..]),
                    None => false
                }
            }
        }
    }
}

pub fn preconditions_ok(request: &Request, etag: Option<&str>) -> bool {
    match request.headers.get::<header::IfNoneMatch>() {
        Some(header) => if header.matches_etag(etag) { return false; },
        None => ()
    };

    match request.headers.get::<header::IfMatch>() {
        Some(header) => if !header.matches_etag(etag) { return false; },
        None => ()
    };
    true
}

pub trait FormDataHelper<K: ?Sized, V> {
    fn get_only<Q: AsRef<K>>(&self, k: Q) -> Option<&String>;
}

impl FormDataHelper<str, String> for urlencoded::QueryMap {
    fn get_only<Q: AsRef<str>>(&self, k: Q) -> Option<&String> {
        match self.get(&k.as_ref().to_owned()) {
            Some(x) if x.len() == 1 => Some(&x[0]),
            _ => None
        }
    }
}

pub fn get_account_id(user: &models::User, request: &Request) -> String {
    let url = request.url.clone().into_generic_url();
    let scheme_data = match url.scheme_data {
        SchemeData::Relative(ref x) => x,
        _ => panic!("Expected relative scheme data.")
    };

    let mut rv = format!("{}@{}", user.userid, scheme_data.host);
    if let Some(port) = scheme_data.port {
        if Some(port) != scheme_data.default_port {
            rv.push_str(&format!(":{}", port)[..]);
        }
    };

    rv
}
<|endoftext|>


<commit_before>use super::Parser;
use ::errors;
use ::GameDescriptor;
use ::default_game_descriptor::DefaultGameDescriptor;
use std::io::{Read, BufRead, BufReader};
use regex::Regex;

/// Parser for files in the life 1.05 format.
pub struct Life106Parser {}

impl Life106Parser {
	/// Construct a new instance.
	pub fn new() -> Self {
		Life106Parser{}
	}
}

impl Parser for Life106Parser {
	fn parse(&mut self, input: Box<Read>) -> errors::Result<Box<GameDescriptor>> {
		let reader = BufReader::new(input);
		let mut ret = DefaultGameDescriptor::new();
		let regex = Regex::new("(\\d+)\\s+(\\d+)")
			.expect("invalid regex!");

		for line in reader.lines() {
			let line = line.
				map_err(|err| errors::ErrorKind::IOError(err.kind()))?;
			let line = line.trim();

			if regex.is_match(line) {
				let coords: Vec<i16> = line.split_whitespace()
					.map(|it| it.parse::<i16>().expect("Error parsing int!"))
					.collect();
				ret.add_live_cell(coords[0], coords[1]);
			}
		}

		Ok(Box::new(ret))
	}
}

#[cfg(test)]
mod test {
	extern crate io_test_util;

	use super::*;
	use std::io;

	#[test]
	fn should_correctly_handle_io_errors() {
		use self::io_test_util;

		let mut parser = Life106Parser::new();
		let reader = io_test_util::ErrReader::new(io::ErrorKind::NotFound);

		if let Err(parser_error) = parser.parse(Box::new(reader)) {
			match *parser_error.kind() {
				errors::ErrorKind::IOError(inner_error) => {
					assert_eq!(io::ErrorKind::NotFound, inner_error);
				},
				_ => panic!("wrong error kind!")
			}
		} else {
			panic!("No error returned!")
		}
	}
}
<commit_msg>added file format validation to life 1.06 parser<commit_after>use super::Parser;
use ::errors::{self, ErrorKind};
use ::GameDescriptor;
use ::default_game_descriptor::DefaultGameDescriptor;
use std::io::{Read, BufRead, BufReader};
use regex::Regex;

/// Parser for files in the life 1.05 format.
pub struct Life106Parser {}

impl Life106Parser {
	/// Construct a new instance.
	pub fn new() -> Self {
		Life106Parser{}
	}
}

impl Parser for Life106Parser {
	fn parse(&mut self, input: Box<Read>) -> errors::Result<Box<GameDescriptor>> {
		let reader = BufReader::new(input);
		let mut ret = DefaultGameDescriptor::new();
		let regex = Regex::new("(\\d+)\\s+(\\d+)")
			.expect("invalid regex!");

		for line in reader.lines() {
			let line = line.
				map_err(|err| errors::ErrorKind::IOError(err.kind()))?;
			let line = line.trim();

			if line.starts_with("#Life") {
				check_file_format(line)?;
			} else if regex.is_match(line) {
				let coords: Vec<i16> = line.split_whitespace()
					.map(|it| it.parse::<i16>().expect("Error parsing int!"))
					.collect();
				ret.add_live_cell(coords[0], coords[1]);
			}
		}

		Ok(Box::new(ret))
	}
}

fn check_file_format(line: &str) -> errors::Result<()> {
	use regex::Regex;

	let regex = Regex::new("#Life\\s+1.06").expect("Invalid regex");
	if regex.is_match(line) {
		Ok(())
	} else {
		bail!(ErrorKind::InvalidFileFormat)
	}
}

#[cfg(test)]
mod test {
	extern crate io_test_util;

	use super::*;
	use std::io;

	#[test]
	fn should_correctly_handle_io_errors() {
		use self::io_test_util;

		let mut parser = Life106Parser::new();
		let reader = io_test_util::ErrReader::new(io::ErrorKind::NotFound);

		if let Err(parser_error) = parser.parse(Box::new(reader)) {
			match *parser_error.kind() {
				errors::ErrorKind::IOError(inner_error) => {
					assert_eq!(io::ErrorKind::NotFound, inner_error);
				},
				_ => panic!("wrong error kind!")
			}
		} else {
			panic!("No error returned!")
		}
	}

	#[test]
	fn should_throw_error_on_wrong_format_annotation() {
		let mut parser = Life106Parser::new();
		let input = Box::new("#Life 1.05\n#P -1 -1\n.*.".as_bytes());
		let res = parser.parse(input);
		match res {
			Err(errors::Error(errors::ErrorKind::InvalidFileFormat, _)) => {},
			Err(errors::Error(x, _)) => panic!("Unexpected error {}", x),
			_ => panic!("No error thrown!"),

		}
	}
}
<|endoftext|>