<commit_before>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// 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.

use std::sync::Arc;
use std::path::Path;

use hab_core::util::sys;
use hyper::client::Client as HyperClient;
use hyper::client::RequestBuilder;
use hyper::client::pool::{Config, Pool};
use hyper::header::UserAgent;
use hyper::http::h1::Http11Protocol;
use hyper::net::{HttpsConnector, Openssl};
use openssl::ssl::{SslContext, SslMethod, SSL_OP_NO_SSLV2, SSL_OP_NO_SSLV3, SSL_OP_NO_COMPRESSION};
use url::Url;

use error::Result;
use net::ProxyHttpsConnector;
use proxy::{ProxyInfo, proxy_unless_domain_exempted};
use ssl;

header! { (ProxyAuthorization, "Proxy-Authorization") => [String] }

/// A generic wrapper around a Hyper HTTP client intended for API-like usage.
///
/// When an `ApiClient` is created, it has a constant URL base which is assumed to be some API
/// endpoint. This allows the underlying Hyper client to load and use any relevant HTTP proxy
/// support and to provide reasonable User-Agent HTTP headers, etc.
#[derive(Debug)]
pub struct ApiClient {
    /// The base URL for the client.
    endpoint: Url,
    /// An instance of a `hyper::Client` which is configured with an SSL context and optionally
    /// using an HTTP proxy.
    inner: HyperClient,
    /// Proxy information, if a proxy is being used.
    proxy: Option<ProxyInfo>,
    /// The URL scheme of the endpoint.
    target_scheme: String,
    /// The `User-Agent` header string to use for HTTP calls.
    user_agent_header: UserAgent,
}

impl ApiClient {
    /// Creates and returns a new `ApiClient` instance.
    ///
    /// # Errors
    ///
    /// * If the underlying Hyper client cannot be created
    /// * If a suitable SSL context cannot be established
    /// * If an HTTP proxy cannot be correctly setup
    /// * If a `User-Agent` HTTP header string cannot be constructed
    pub fn new(endpoint: &Url,
               product: &str,
               version: &str,
               fs_root_path: Option<&Path>)
               -> Result<Self> {
        Ok(ApiClient {
            endpoint: endpoint.clone(),
            inner: try!(new_hyper_client(Some(endpoint), fs_root_path)),
            proxy: try!(proxy_unless_domain_exempted(Some(endpoint))),
            target_scheme: endpoint.scheme().to_string(),
            user_agent_header: try!(user_agent(product, version)),
        })
    }

    /// Builds an HTTP GET request for a given path.
    pub fn get(&self, path: &str) -> RequestBuilder {
        self.get_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP GET request for a given path with the ability to customize the target URL.
    pub fn get_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("GET {} with {:?}", &url, &self);
        self.add_headers(self.inner.get(url))
    }

    /// Builds an HTTP HEAD request for a given path.
    pub fn head(&self, path: &str) -> RequestBuilder {
        self.head_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP HEAD request for a given path with the ability to customize the target URL.
    pub fn head_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("HEAD {} with {:?}", &url, &self);
        self.add_headers(self.inner.head(url))
    }

    /// Builds an HTTP PATCH request for a given path.
    pub fn patch(&self, path: &str) -> RequestBuilder {
        self.patch_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PATCH request for a given path with the ability to customize the target URL.
    pub fn patch_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PATH {} with {:?}", &url, &self);
        self.add_headers(self.inner.patch(url))
    }

    /// Builds an HTTP POST request for a given path.
    pub fn post(&self, path: &str) -> RequestBuilder {
        self.post_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP POST request for a given path with the ability to customize the target URL.
    pub fn post_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("POST {} with {:?}", &url, &self);
        self.add_headers(self.inner.post(url))
    }

    /// Builds an HTTP PUT request for a given path.
    pub fn put(&self, path: &str) -> RequestBuilder {
        self.put_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PUT request for a given path with the ability to customize the target URL.
    pub fn put_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PUT {} with {:?}", &url, &self);
        self.add_headers(self.inner.put(url))
    }

    /// Builds an HTTP DELETE request for a given path.
    pub fn delete(&self, path: &str) -> RequestBuilder {
        self.delete_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP DELETE request for a given path with the ability to customize the target URL.
    pub fn delete_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("DELETE {} with {:?}", &url, &self);
        self.add_headers(self.inner.delete(url))
    }

    fn add_headers<'a>(&'a self, rb: RequestBuilder<'a>) -> RequestBuilder {
        let mut rb = rb.header(self.user_agent_header.clone());
        // If the target URL is an `"http"` scheme and we're using a proxy server, then add the
        // proxy authorization header if appropriate. Note that for `"https"` targets, the proxy
        // server will be operating in TCP tuneling mode and will be authenticated on connection to
        // the proxy server which is why we should not add an additional header in this latter
        // case.
        if self.target_scheme == "http" {
            if let Some(ref info) = self.proxy {
                if let Some(header_value) = info.authorization_header_value() {
                    rb = rb.header(ProxyAuthorization(header_value));
                }
            }
        }
        rb
    }

    fn url_for(&self, path: &str) -> Url {
        let mut url = self.endpoint.clone();
        if path.is_empty() {
            return url;
        }
        if url.path().ends_with("/") {
            url.set_path(&format!("{}{}", self.endpoint.path(), path));
        } else {
            url.set_path(&format!("{}/{}", self.endpoint.path(), path));
        }
        url
    }
}

/// Builds a new hyper HTTP client with appropriate SSL configuration and HTTP/HTTPS proxy support.
///
/// ## Linux Platforms
///
/// We need a set of root certificates when connected to SSL/TLS web endpoints and this usually
/// boild down to using an all-in-one certificate file (such as a `cacert.pem` file) or a directory
/// of files which are certificates. The strategy to location or use a reasonable set of
/// certificates is as follows:
///
/// 1. If the `SSL_CERT_FILE` environment variable is set, then use its value for the certificates.
///    Interally this is triggering default OpenSSL behavior for this environment variable.
/// 2. If the `SSL_CERT_DIR` environment variable is set, then use its value for the directory
///    containing certificates. Like the `SSL_CERT_FILE` case above, this triggers default OpenSSL
///    behavior for this environment variable.
/// 3. If the `core/cacerts` Habitat package is installed locally, then use the latest release's
///    `cacert.pem` file.
/// 4. If none of the conditions above are met, then a `cacert.pem` will be written in an SSL cache
///    directory (by default `/hab/cache/ssl` for a root user and `$HOME/.hab/cache/ssl` for a
///    non-root user) and this will be used. The contents of this file will be inlined in this
///    crate at build time as a fallback insurance policy, meaning that if the a program using this
///    code is operating in a minimal environment which may not contain system certificates, it can
///    still operate. Once a `core/cacerts` Habitat package is present, the logic would fall back
///    to preferring the package version to the cached/inline file version.
///
/// ## Mac Platforms
///
/// The Mac platoform uses a Security Framework to store and find root certificates and the hyper
/// library will default to using this on the Mac. Therefore the behavior on the Mac remains
/// unchanged and will use the system's certificates.
///
fn new_hyper_client(for_domain: Option<&Url>, fs_root_path: Option<&Path>) -> Result<HyperClient> {
    let ctx = try!(ssl_ctx(fs_root_path));
    let ssl_client = Openssl { context: Arc::new(ctx) };
    match try!(proxy_unless_domain_exempted(for_domain)) {
        Some(proxy) => {
            debug!("Using proxy {}:{}...", proxy.host(), proxy.port());
            let connector = try!(ProxyHttpsConnector::new(proxy, ssl_client));
            let pool = Pool::with_connector(Config::default(), connector);
            Ok(HyperClient::with_protocol(Http11Protocol::with_connector(pool)))
        }
        None => {
            let connector = HttpsConnector::new(ssl_client);
            let pool = Pool::with_connector(Config::default(), connector);
            Ok(HyperClient::with_protocol(Http11Protocol::with_connector(pool)))
        }
    }
}

/// Returns an HTTP User-Agent string type for use by Hyper when making HTTP requests.
///
/// The general form for Habitat-related clients are of the following form:
///
/// ```text
/// <PRODUCT>/<VERSION> (<TARGET>; <KERNEL_RELEASE>)
/// ```
///
/// where:
///
/// * `<PRODUCT>`: is the provided product name
/// * `<VERSION>`: is the provided version string which may also include a release number
/// * `<TARGET>`: is the machine architecture and the kernel seperated by a dash in lower case
/// * `<KERNEL_RELEASE>`: is the kernel release string from `uname`
///
/// For example:
///
/// ```text
/// hab/0.6.0/20160606153031 (x86_64-darwin; 14.5.0)
/// ```
///
/// # Errors
///
/// * If system information cannot be obtained via `uname`
fn user_agent(product: &str, version: &str) -> Result<UserAgent> {
    let uname = try!(sys::uname());
    let ua = format!("{}/{} ({}-{}; {})",
                     product.trim(),
                     version.trim(),
                     uname.machine.trim().to_lowercase(),
                     uname.sys_name.trim().to_lowercase(),
                     uname.release.trim().to_lowercase());
    debug!("User-Agent: {}", &ua);
    Ok(UserAgent(ua))
}

fn ssl_ctx(fs_root_path: Option<&Path>) -> Result<SslContext> {
    // The spirit of this implementation is directly from Hyper's default OpensslClient function:
    // https://github.com/hyperium/hyper/blob/v0.9.5/src/net.rs#L653-L661
    let mut ctx = try!(SslContext::new(SslMethod::Sslv23));
    try!(ssl::set_ca(&mut ctx, fs_root_path));
    ctx.set_options(SSL_OP_NO_SSLV2 | SSL_OP_NO_SSLV3 | SSL_OP_NO_COMPRESSION);
    try!(ctx.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH"));
    Ok(ctx)
}
<commit_msg>Retry package downloads<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// 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.

use std::sync::Arc;
use std::path::Path;
use std::time::Duration;

use hab_core::util::sys;
use hyper::client::Client as HyperClient;
use hyper::client::RequestBuilder;
use hyper::client::pool::{Config, Pool};
use hyper::header::UserAgent;
use hyper::http::h1::Http11Protocol;
use hyper::net::{HttpsConnector, Openssl};
use openssl::ssl::{SslContext, SslMethod, SSL_OP_NO_SSLV2, SSL_OP_NO_SSLV3, SSL_OP_NO_COMPRESSION};
use url::Url;

use error::Result;
use net::ProxyHttpsConnector;
use proxy::{ProxyInfo, proxy_unless_domain_exempted};
use ssl;

header! { (ProxyAuthorization, "Proxy-Authorization") => [String] }

/// A generic wrapper around a Hyper HTTP client intended for API-like usage.
///
/// When an `ApiClient` is created, it has a constant URL base which is assumed to be some API
/// endpoint. This allows the underlying Hyper client to load and use any relevant HTTP proxy
/// support and to provide reasonable User-Agent HTTP headers, etc.
#[derive(Debug)]
pub struct ApiClient {
    /// The base URL for the client.
    endpoint: Url,
    /// An instance of a `hyper::Client` which is configured with an SSL context and optionally
    /// using an HTTP proxy.
    inner: HyperClient,
    /// Proxy information, if a proxy is being used.
    proxy: Option<ProxyInfo>,
    /// The URL scheme of the endpoint.
    target_scheme: String,
    /// The `User-Agent` header string to use for HTTP calls.
    user_agent_header: UserAgent,
}

impl ApiClient {
    /// Creates and returns a new `ApiClient` instance.
    ///
    /// # Errors
    ///
    /// * If the underlying Hyper client cannot be created
    /// * If a suitable SSL context cannot be established
    /// * If an HTTP proxy cannot be correctly setup
    /// * If a `User-Agent` HTTP header string cannot be constructed
    pub fn new(endpoint: &Url,
               product: &str,
               version: &str,
               fs_root_path: Option<&Path>)
               -> Result<Self> {
        Ok(ApiClient {
            endpoint: endpoint.clone(),
            inner: try!(new_hyper_client(Some(endpoint), fs_root_path)),
            proxy: try!(proxy_unless_domain_exempted(Some(endpoint))),
            target_scheme: endpoint.scheme().to_string(),
            user_agent_header: try!(user_agent(product, version)),
        })
    }

    /// Builds an HTTP GET request for a given path.
    pub fn get(&self, path: &str) -> RequestBuilder {
        self.get_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP GET request for a given path with the ability to customize the target URL.
    pub fn get_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("GET {} with {:?}", &url, &self);
        self.add_headers(self.inner.get(url))
    }

    /// Builds an HTTP HEAD request for a given path.
    pub fn head(&self, path: &str) -> RequestBuilder {
        self.head_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP HEAD request for a given path with the ability to customize the target URL.
    pub fn head_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("HEAD {} with {:?}", &url, &self);
        self.add_headers(self.inner.head(url))
    }

    /// Builds an HTTP PATCH request for a given path.
    pub fn patch(&self, path: &str) -> RequestBuilder {
        self.patch_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PATCH request for a given path with the ability to customize the target URL.
    pub fn patch_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PATH {} with {:?}", &url, &self);
        self.add_headers(self.inner.patch(url))
    }

    /// Builds an HTTP POST request for a given path.
    pub fn post(&self, path: &str) -> RequestBuilder {
        self.post_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP POST request for a given path with the ability to customize the target URL.
    pub fn post_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("POST {} with {:?}", &url, &self);
        self.add_headers(self.inner.post(url))
    }

    /// Builds an HTTP PUT request for a given path.
    pub fn put(&self, path: &str) -> RequestBuilder {
        self.put_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PUT request for a given path with the ability to customize the target URL.
    pub fn put_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PUT {} with {:?}", &url, &self);
        self.add_headers(self.inner.put(url))
    }

    /// Builds an HTTP DELETE request for a given path.
    pub fn delete(&self, path: &str) -> RequestBuilder {
        self.delete_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP DELETE request for a given path with the ability to customize the target URL.
    pub fn delete_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
        where F: FnMut(&mut Url)
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("DELETE {} with {:?}", &url, &self);
        self.add_headers(self.inner.delete(url))
    }

    fn add_headers<'a>(&'a self, rb: RequestBuilder<'a>) -> RequestBuilder {
        let mut rb = rb.header(self.user_agent_header.clone());
        // If the target URL is an `"http"` scheme and we're using a proxy server, then add the
        // proxy authorization header if appropriate. Note that for `"https"` targets, the proxy
        // server will be operating in TCP tuneling mode and will be authenticated on connection to
        // the proxy server which is why we should not add an additional header in this latter
        // case.
        if self.target_scheme == "http" {
            if let Some(ref info) = self.proxy {
                if let Some(header_value) = info.authorization_header_value() {
                    rb = rb.header(ProxyAuthorization(header_value));
                }
            }
        }
        rb
    }

    fn url_for(&self, path: &str) -> Url {
        let mut url = self.endpoint.clone();
        if path.is_empty() {
            return url;
        }
        if url.path().ends_with("/") {
            url.set_path(&format!("{}{}", self.endpoint.path(), path));
        } else {
            url.set_path(&format!("{}/{}", self.endpoint.path(), path));
        }
        url
    }
}

/// Builds a new hyper HTTP client with appropriate SSL configuration and HTTP/HTTPS proxy support.
///
/// ## Linux Platforms
///
/// We need a set of root certificates when connected to SSL/TLS web endpoints and this usually
/// boild down to using an all-in-one certificate file (such as a `cacert.pem` file) or a directory
/// of files which are certificates. The strategy to location or use a reasonable set of
/// certificates is as follows:
///
/// 1. If the `SSL_CERT_FILE` environment variable is set, then use its value for the certificates.
///    Interally this is triggering default OpenSSL behavior for this environment variable.
/// 2. If the `SSL_CERT_DIR` environment variable is set, then use its value for the directory
///    containing certificates. Like the `SSL_CERT_FILE` case above, this triggers default OpenSSL
///    behavior for this environment variable.
/// 3. If the `core/cacerts` Habitat package is installed locally, then use the latest release's
///    `cacert.pem` file.
/// 4. If none of the conditions above are met, then a `cacert.pem` will be written in an SSL cache
///    directory (by default `/hab/cache/ssl` for a root user and `$HOME/.hab/cache/ssl` for a
///    non-root user) and this will be used. The contents of this file will be inlined in this
///    crate at build time as a fallback insurance policy, meaning that if the a program using this
///    code is operating in a minimal environment which may not contain system certificates, it can
///    still operate. Once a `core/cacerts` Habitat package is present, the logic would fall back
///    to preferring the package version to the cached/inline file version.
///
/// ## Mac Platforms
///
/// The Mac platoform uses a Security Framework to store and find root certificates and the hyper
/// library will default to using this on the Mac. Therefore the behavior on the Mac remains
/// unchanged and will use the system's certificates.
///
fn new_hyper_client(for_domain: Option<&Url>, fs_root_path: Option<&Path>) -> Result<HyperClient> {
    let ctx = try!(ssl_ctx(fs_root_path));
    let ssl_client = Openssl { context: Arc::new(ctx) };
    match try!(proxy_unless_domain_exempted(for_domain)) {
        Some(proxy) => {
            debug!("Using proxy {}:{}...", proxy.host(), proxy.port());
            let connector = try!(ProxyHttpsConnector::new(proxy, ssl_client));
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            let timeout = Some(Duration::from_secs(5));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
        None => {
            let connector = HttpsConnector::new(ssl_client);
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            let timeout = Some(Duration::from_secs(5));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
    }
}

/// Returns an HTTP User-Agent string type for use by Hyper when making HTTP requests.
///
/// The general form for Habitat-related clients are of the following form:
///
/// ```text
/// <PRODUCT>/<VERSION> (<TARGET>; <KERNEL_RELEASE>)
/// ```
///
/// where:
///
/// * `<PRODUCT>`: is the provided product name
/// * `<VERSION>`: is the provided version string which may also include a release number
/// * `<TARGET>`: is the machine architecture and the kernel seperated by a dash in lower case
/// * `<KERNEL_RELEASE>`: is the kernel release string from `uname`
///
/// For example:
///
/// ```text
/// hab/0.6.0/20160606153031 (x86_64-darwin; 14.5.0)
/// ```
///
/// # Errors
///
/// * If system information cannot be obtained via `uname`
fn user_agent(product: &str, version: &str) -> Result<UserAgent> {
    let uname = try!(sys::uname());
    let ua = format!("{}/{} ({}-{}; {})",
                     product.trim(),
                     version.trim(),
                     uname.machine.trim().to_lowercase(),
                     uname.sys_name.trim().to_lowercase(),
                     uname.release.trim().to_lowercase());
    debug!("User-Agent: {}", &ua);
    Ok(UserAgent(ua))
}

fn ssl_ctx(fs_root_path: Option<&Path>) -> Result<SslContext> {
    // The spirit of this implementation is directly from Hyper's default OpensslClient function:
    // https://github.com/hyperium/hyper/blob/v0.9.5/src/net.rs#L653-L661
    let mut ctx = try!(SslContext::new(SslMethod::Sslv23));
    try!(ssl::set_ca(&mut ctx, fs_root_path));
    ctx.set_options(SSL_OP_NO_SSLV2 | SSL_OP_NO_SSLV3 | SSL_OP_NO_COMPRESSION);
    try!(ctx.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH"));
    Ok(ctx)
}
<|endoftext|>


<commit_before>use ast::*;
use ast::Stmt::*;
use ast::visit::*;
use ctxt::{Context, Fct, FctId, FctParent, FctSrc};
use error::msg::Msg;
use semck;
use ty::BuiltinType;

pub fn check<'a, 'ast>(ctxt: &Context<'ast>) {
    for fct in &ctxt.fcts {
        let mut fct = fct.borrow_mut();
        let ast = fct.ast;

        if !(fct.is_src() || fct.kind.is_definition()) {
            continue;
        }

        match fct.parent {
            FctParent::Class(owner_class) => {
                let cls = ctxt.classes[owner_class].borrow();
                fct.param_types.push(cls.ty);
            }

            FctParent::Impl(impl_id) => {
                let ximpl = ctxt.impls[impl_id].borrow();
                let cls = ctxt.classes[ximpl.cls_id()].borrow();
                fct.param_types.push(cls.ty);
            }

            FctParent::Trait(_) => {
                fct.param_types.push(BuiltinType::This);
            }

            _ => {}
        }

        for p in &ast.params {
            let ty = semck::read_type(ctxt, &p.data_type).unwrap_or(BuiltinType::Unit);

            if ty == BuiltinType::This && !fct.in_trait() {
                ctxt.diag.borrow_mut().report(p.data_type.pos(), Msg::SelfTypeUnavailable);
            }

            fct.param_types.push(ty);

            if fct.is_src() {
                let src = fct.src();
                let mut src = src.lock().unwrap();

                let var = *src.map_vars.get(p.id).unwrap();
                src.vars[var].ty = ty;
            }
        }

        if let Some(ret) = ast.return_type.as_ref() {
            let ty = semck::read_type(ctxt, ret).unwrap_or(BuiltinType::Unit);
            fct.return_type = ty;
        }

        fct.initialized = true;

        match fct.parent {
            FctParent::Class(clsid) => {
                let cls = ctxt.classes[clsid].borrow();
                check_against_methods(ctxt, cls.ty, &*fct, &cls.methods);
            }

            FctParent::Trait(traitid) => {
                let xtrait = ctxt.traits[traitid].borrow();
                let ty = BuiltinType::Trait(traitid);
                check_against_methods(ctxt, ty, &*fct, &xtrait.methods);
            }

            FctParent::Impl(implid) => {
                let ximpl = ctxt.impls[implid].borrow();
                let ty = BuiltinType::Trait(ximpl.trait_id());
                check_against_methods(ctxt, ty, &*fct, &ximpl.methods);
            }

            _ => {}
        }

        if !fct.is_src() {
            continue;
        }

        let src = fct.src();
        let mut src = src.lock().unwrap();

        let mut defck = FctDefCheck {
            ctxt: ctxt,
            fct: &mut *fct,
            src: &mut src,
            ast: ast,
            current_type: BuiltinType::Unit,
        };

        defck.check();
    }
}

fn check_against_methods(ctxt: &Context, ty: BuiltinType, fct: &Fct, methods: &[FctId]) {
    for &method in methods {
        if method == fct.id {
            continue;
        }

        let method = ctxt.fcts[method].borrow();

        if method.initialized && method.name == fct.name &&
           method.params_with_self() == fct.params_with_self() {
            let cls_name = ty.name(ctxt);
            let param_names = method.params_without_self()
                .iter()
                .map(|a| a.name(ctxt))
                .collect::<Vec<String>>();
            let method_name = ctxt.interner.str(method.name).to_string();

            let msg = Msg::MethodExists(cls_name, method_name, param_names, method.pos);
            ctxt.diag.borrow_mut().report(fct.ast.pos, msg);
            return;
        }
    }
}

struct FctDefCheck<'a, 'ast: 'a> {
    ctxt: &'a Context<'ast>,
    fct: &'a mut Fct<'ast>,
    src: &'a mut FctSrc<'ast>,
    ast: &'ast Function,
    current_type: BuiltinType,
}

impl<'a, 'ast> FctDefCheck<'a, 'ast> {
    fn check(&mut self) {
        self.visit_fct(self.ast);
    }
}

impl<'a, 'ast> Visitor<'ast> for FctDefCheck<'a, 'ast> {
    fn visit_fct(&mut self, f: &'ast Function) {
        self.visit_stmt(f.block());
    }

    fn visit_stmt(&mut self, s: &'ast Stmt) {
        match *s {
            StmtVar(ref var) => {
                if let Some(ref data_type) = var.data_type {
                    self.visit_type(data_type);

                    let varid = *self.src.map_vars.get(var.id).unwrap();
                    self.src.vars[varid].ty = self.current_type;
                }

                if let Some(ref expr) = var.expr {
                    visit::walk_expr(self, expr);
                }

            }

            StmtDo(ref try) => {
                for catch in &try.catch_blocks {
                    self.visit_type(&catch.data_type);
                    self.src.set_ty(catch.id, self.current_type);

                    let var = *self.src.map_vars.get(catch.id).unwrap();
                    self.src.vars[var].ty = self.current_type;

                    if !self.current_type.reference_type() {
                        let ty = self.current_type.name(self.ctxt);
                        self.ctxt
                            .diag
                            .borrow_mut()
                            .report(catch.data_type.pos(), Msg::ReferenceTypeExpected(ty));
                    }
                }

                if try.catch_blocks.is_empty() && try.finally_block.is_none() {
                    self.ctxt.diag.borrow_mut().report(try.pos, Msg::CatchOrFinallyExpected);
                }

                visit::walk_stmt(self, s);
            }

            _ => visit::walk_stmt(self, s),
        }
    }

    fn visit_type(&mut self, t: &'ast Type) {
        self.current_type = semck::read_type(self.ctxt, t).unwrap_or(BuiltinType::Unit);
    }
}
<commit_msg>check Self for return type<commit_after>use ast::*;
use ast::Stmt::*;
use ast::visit::*;
use ctxt::{Context, Fct, FctId, FctParent, FctSrc};
use error::msg::Msg;
use semck;
use ty::BuiltinType;

pub fn check<'a, 'ast>(ctxt: &Context<'ast>) {
    for fct in &ctxt.fcts {
        let mut fct = fct.borrow_mut();
        let ast = fct.ast;

        if !(fct.is_src() || fct.kind.is_definition()) {
            continue;
        }

        match fct.parent {
            FctParent::Class(owner_class) => {
                let cls = ctxt.classes[owner_class].borrow();
                fct.param_types.push(cls.ty);
            }

            FctParent::Impl(impl_id) => {
                let ximpl = ctxt.impls[impl_id].borrow();
                let cls = ctxt.classes[ximpl.cls_id()].borrow();
                fct.param_types.push(cls.ty);
            }

            FctParent::Trait(_) => {
                fct.param_types.push(BuiltinType::This);
            }

            _ => {}
        }

        for p in &ast.params {
            let ty = semck::read_type(ctxt, &p.data_type).unwrap_or(BuiltinType::Unit);

            if ty == BuiltinType::This && !fct.in_trait() {
                ctxt.diag.borrow_mut().report(p.data_type.pos(), Msg::SelfTypeUnavailable);
            }

            fct.param_types.push(ty);

            if fct.is_src() {
                let src = fct.src();
                let mut src = src.lock().unwrap();

                let var = *src.map_vars.get(p.id).unwrap();
                src.vars[var].ty = ty;
            }
        }

        if let Some(ret) = ast.return_type.as_ref() {
            let ty = semck::read_type(ctxt, ret).unwrap_or(BuiltinType::Unit);

            if ty == BuiltinType::This && !fct.in_trait() {
                ctxt.diag.borrow_mut().report(ret.pos(), Msg::SelfTypeUnavailable);
            }

            fct.return_type = ty;
        }

        fct.initialized = true;

        match fct.parent {
            FctParent::Class(clsid) => {
                let cls = ctxt.classes[clsid].borrow();
                check_against_methods(ctxt, cls.ty, &*fct, &cls.methods);
            }

            FctParent::Trait(traitid) => {
                let xtrait = ctxt.traits[traitid].borrow();
                let ty = BuiltinType::Trait(traitid);
                check_against_methods(ctxt, ty, &*fct, &xtrait.methods);
            }

            FctParent::Impl(implid) => {
                let ximpl = ctxt.impls[implid].borrow();
                let ty = BuiltinType::Trait(ximpl.trait_id());
                check_against_methods(ctxt, ty, &*fct, &ximpl.methods);
            }

            _ => {}
        }

        if !fct.is_src() {
            continue;
        }

        let src = fct.src();
        let mut src = src.lock().unwrap();

        let mut defck = FctDefCheck {
            ctxt: ctxt,
            fct: &mut *fct,
            src: &mut src,
            ast: ast,
            current_type: BuiltinType::Unit,
        };

        defck.check();
    }
}

fn check_against_methods(ctxt: &Context, ty: BuiltinType, fct: &Fct, methods: &[FctId]) {
    for &method in methods {
        if method == fct.id {
            continue;
        }

        let method = ctxt.fcts[method].borrow();

        if method.initialized && method.name == fct.name &&
           method.params_with_self() == fct.params_with_self() {
            let cls_name = ty.name(ctxt);
            let param_names = method.params_without_self()
                .iter()
                .map(|a| a.name(ctxt))
                .collect::<Vec<String>>();
            let method_name = ctxt.interner.str(method.name).to_string();

            let msg = Msg::MethodExists(cls_name, method_name, param_names, method.pos);
            ctxt.diag.borrow_mut().report(fct.ast.pos, msg);
            return;
        }
    }
}

struct FctDefCheck<'a, 'ast: 'a> {
    ctxt: &'a Context<'ast>,
    fct: &'a mut Fct<'ast>,
    src: &'a mut FctSrc<'ast>,
    ast: &'ast Function,
    current_type: BuiltinType,
}

impl<'a, 'ast> FctDefCheck<'a, 'ast> {
    fn check(&mut self) {
        self.visit_fct(self.ast);
    }
}

impl<'a, 'ast> Visitor<'ast> for FctDefCheck<'a, 'ast> {
    fn visit_fct(&mut self, f: &'ast Function) {
        self.visit_stmt(f.block());
    }

    fn visit_stmt(&mut self, s: &'ast Stmt) {
        match *s {
            StmtVar(ref var) => {
                if let Some(ref data_type) = var.data_type {
                    self.visit_type(data_type);

                    let varid = *self.src.map_vars.get(var.id).unwrap();
                    self.src.vars[varid].ty = self.current_type;
                }

                if let Some(ref expr) = var.expr {
                    visit::walk_expr(self, expr);
                }

            }

            StmtDo(ref try) => {
                for catch in &try.catch_blocks {
                    self.visit_type(&catch.data_type);
                    self.src.set_ty(catch.id, self.current_type);

                    let var = *self.src.map_vars.get(catch.id).unwrap();
                    self.src.vars[var].ty = self.current_type;

                    if !self.current_type.reference_type() {
                        let ty = self.current_type.name(self.ctxt);
                        self.ctxt
                            .diag
                            .borrow_mut()
                            .report(catch.data_type.pos(), Msg::ReferenceTypeExpected(ty));
                    }
                }

                if try.catch_blocks.is_empty() && try.finally_block.is_none() {
                    self.ctxt.diag.borrow_mut().report(try.pos, Msg::CatchOrFinallyExpected);
                }

                visit::walk_stmt(self, s);
            }

            _ => visit::walk_stmt(self, s),
        }
    }

    fn visit_type(&mut self, t: &'ast Type) {
        self.current_type = semck::read_type(self.ctxt, t).unwrap_or(BuiltinType::Unit);
    }
}

#[cfg(test)]
mod tests {
    use error::msg::Msg;
    use semck::tests::*;

    #[test]
    fn self_param() {
        err("fun foo(x: Self) {}",
            pos(1, 12),
            Msg::SelfTypeUnavailable);
    }

    #[test]
    fn self_return_type() {
        err("fun foo() -> Self {}",
            pos(1, 14),
            Msg::SelfTypeUnavailable);
    }
}<|endoftext|>


<commit_before>use serde::ser::Serialize;
use serde::ser::Serializer;

trait EntryType: Serialize {
    fn type_name() -> &'static str;
    fn remote_ip(&self) -> &str;
    fn timestamp(&self) -> f64;
    fn request_method(&self) -> &str;
    fn request_url(&self) -> &str;
    fn request_protocol(&self) -> &str;
    fn response_status(&self) -> Option<u32>;
    fn response_bytes(&self) -> Option<u64>;
}

#[derive(Serialize, Debug)]
struct ClientAccessLogEntry<'a> {
    record_type: &'a str,
    vxid: u32,
    request_type: &'a str,
    remote_address: AddressLogEntry<'a>,
    session_timestamp: f64,
    start_timestamp: f64,
    end_timestamp: f64,
    handing: &'a str,
    request: HttpRequestLogEntry<'a>,
    response: HttpResponseLogEntry<'a>,
    process_duration: Option<f64>,
    fetch_duration: Option<f64>,
    ttfb_duration: f64,
    serve_duration: f64,
    recv_header_bytes: u64,
    recv_body_bytes: u64,
    recv_total_bytes: u64,
    sent_header_bytes: u64,
    sent_body_bytes: u64,
    sent_total_bytes: u64,
    esi_count: usize,
    restart_count: usize,
    restart_log: Option<LogBook<'a>>,
    log: LogBook<'a>,
}

impl<'a> EntryType for ClientAccessLogEntry<'a> {
    fn type_name() -> &'static str {
        "client_access"
    }
    fn remote_ip(&self) -> &str {
        self.remote_address.ip
    }
    fn timestamp(&self) -> f64 {
        self.end_timestamp
    }
    fn request_method(&self) -> &str {
        self.request.method
    }
    fn request_url(&self) -> &str {
        self.request.url
    }
    fn request_protocol(&self) -> &str {
        self.request.protocol
    }
    fn response_status(&self) -> Option<u32> {
        Some(self.response.status)
    }
    fn response_bytes(&self) -> Option<u64> {
        Some(self.sent_total_bytes)
    }
}

#[derive(Serialize, Debug)]
struct BackendAccessLogEntry<'a> {
    record_type: &'a str,
    vxid: u32,
    remote_address: AddressLogEntry<'a>,
    session_timestamp: f64,
    start_timestamp: f64,
    end_timestamp: f64,
    handing: &'a str,
    request: HttpRequestLogEntry<'a>,
    response: Option<HttpResponseLogEntry<'a>>,
    send_duration: f64,
    wait_duration: Option<f64>,
    ttfb_duration: Option<f64>,
    fetch_duration: Option<f64>,
    sent_header_bytes: Option<u64>,
    sent_body_bytes: Option<u64>,
    sent_total_bytes: Option<u64>,
    recv_header_bytes: Option<u64>,
    recv_body_bytes: Option<u64>,
    recv_total_bytes: Option<u64>,
    retry: usize,
    backend_connection: Option<BackendConnectionLogEntry<'a>>,
    cache_object: Option<CacheObjectLogEntry<'a>>,
    log: LogBook<'a>,
}

impl<'a> EntryType for BackendAccessLogEntry<'a> {
    fn type_name() -> &'static str {
        "backend_access"
    }
    fn remote_ip(&self) -> &str {
        self.remote_address.ip
    }
    fn timestamp(&self) -> f64 {
        self.end_timestamp
    }
    fn request_method(&self) -> &str {
        self.request.method
    }
    fn request_url(&self) -> &str {
        self.request.url
    }
    fn request_protocol(&self) -> &str {
        self.request.protocol
    }
    fn response_status(&self) -> Option<u32> {
        self.response.as_ref().map(|r| r.status).or(Some(503)) // no response
    }
    fn response_bytes(&self) -> Option<u64> {
        self.recv_total_bytes
    }
}

#[derive(Serialize, Debug)]
struct PipeSessionLogEntry<'a> {
    record_type: &'a str,
    vxid: u32,
    remote_address: AddressLogEntry<'a>,
    session_timestamp: f64,
    start_timestamp: f64,
    end_timestamp: f64,
    backend_connection: BackendConnectionLogEntry<'a>,
    request: HttpRequestLogEntry<'a>,
    backend_request: HttpRequestLogEntry<'a>,
    process_duration: Option<f64>,
    ttfb_duration: f64,
    recv_total_bytes: u64,
    sent_total_bytes: u64,
    log: LogBook<'a>,
}

impl<'a> EntryType for PipeSessionLogEntry<'a> {
    fn type_name() -> &'static str {
        "pipe_session"
    }
    fn remote_ip(&self) -> &str {
        self.remote_address.ip
    }
    fn timestamp(&self) -> f64 {
        self.end_timestamp
    }
    fn request_method(&self) -> &str {
        self.request.method
    }
    fn request_url(&self) -> &str {
        self.request.url
    }
    fn request_protocol(&self) -> &str {
        self.request.protocol
    }
    fn response_status(&self) -> Option<u32> {
        None
    }
    fn response_bytes(&self) -> Option<u64> {
        Some(self.sent_total_bytes)
    }
}

#[derive(Serialize, Debug)]
struct AddressLogEntry<'a> {
    ip: &'a str,
    port: u16,
}

#[derive(Debug)]
struct HeadersLogEntry<'a> {
    headers: &'a [(String, String)],
}

#[derive(Serialize, Debug)]
struct HeaderLogEntry<'a> {
    name: &'a str,
    value: &'a str,
}

impl<'a> Serialize for HeadersLogEntry<'a> {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
        let mut state = try!(serializer.serialize_seq(Some(self.headers.len())));
        for &(ref name, ref value) in self.headers {
            try!(serializer.serialize_seq_elt(&mut state, &HeaderLogEntry {
                name: name.as_str(),
                value: value.as_str(),
            }));
        }
        try!(serializer.serialize_seq_end(state));
        Ok(())
    }
}

#[derive(Serialize, Debug)]
struct HttpRequestLogEntry<'a> {
    protocol: &'a str,
    method: &'a str,
    url: &'a str,
    headers: HeadersLogEntry<'a>,
}

#[derive(Serialize, Debug)]
struct HttpResponseLogEntry<'a> {
    status: u32,
    reason: &'a str,
    protocol: &'a str,
    headers: HeadersLogEntry<'a>,
}

#[derive(Debug)]
struct LogBook<'a> {
    entries: &'a [LogEntry],
}

#[derive(Serialize, Debug)]
struct LogBookEntry<'a> {
    entry_type: &'a str,
    message: &'a str,
    #[serde(skip_serializing_if="Option::is_none")]
    detail: Option<&'a str>,
}

impl<'a> Serialize for LogBook<'a> {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
        let mut state = try!(serializer.serialize_seq(Some(self.entries.len())));
        for log_entry in self.entries {
            let (entry_type, message, detail) = match log_entry {
                &LogEntry::Vcl(ref msg) => ("VCL", msg.as_str(), None),
                &LogEntry::Debug(ref msg) => ("Debug", msg.as_str(), None),
                &LogEntry::Error(ref msg) => ("Error", msg.as_str(), None),
                &LogEntry::FetchError(ref msg) => ("Fetch Error", msg.as_str(), None),
                &LogEntry::Warning(ref msg) => ("Warning", msg.as_str(), None),
                &LogEntry::Acl(ref result, ref name, ref addr) => match result.as_str() {
                    "MATCH" => ("ACL Match", name.as_str(), addr.as_ref().map(String::as_str)),
                    "NO_MATCH" => ("ACL No Match", name.as_str(), addr.as_ref().map(String::as_str)),
                    _ => ("ACL Other", result.as_str(), Some(name.as_str())),
                },
            };

            try!(serializer.serialize_seq_elt(&mut state, &LogBookEntry {
                entry_type: entry_type,
                message: message,
                detail: detail,
            }));
        }
        try!(serializer.serialize_seq_end(state));
        Ok(())
    }
}

#[derive(Serialize, Debug)]
struct CacheObjectLogEntry<'a> {
    storage_type: &'a str,
    storage_name: &'a str,
    ttl_duration: Option<f64>,
    grace_duration: Option<f64>,
    keep_duration: Option<f64>,
    since_timestamp: f64,
    origin_timestamp: f64,
    fetch_mode: &'a str,
    fetch_streamed: bool,
    response: HttpResponseLogEntry<'a>,
}

#[derive(Serialize, Debug)]
struct BackendConnectionLogEntry<'a> {
    fd: isize,
    name: &'a str,
    remote_address: AddressLogEntry<'a>,
    local_address: AddressLogEntry<'a>,
}
<commit_msg>serializing headers as tuples<commit_after>use serde::ser::Serialize;
use serde::ser::Serializer;

trait EntryType: Serialize {
    fn type_name() -> &'static str;
    fn remote_ip(&self) -> &str;
    fn timestamp(&self) -> f64;
    fn request_method(&self) -> &str;
    fn request_url(&self) -> &str;
    fn request_protocol(&self) -> &str;
    fn response_status(&self) -> Option<u32>;
    fn response_bytes(&self) -> Option<u64>;
}

#[derive(Serialize, Debug)]
struct ClientAccessLogEntry<'a> {
    record_type: &'a str,
    vxid: u32,
    request_type: &'a str,
    remote_address: AddressLogEntry<'a>,
    session_timestamp: f64,
    start_timestamp: f64,
    end_timestamp: f64,
    handing: &'a str,
    request: HttpRequestLogEntry<'a>,
    response: HttpResponseLogEntry<'a>,
    process_duration: Option<f64>,
    fetch_duration: Option<f64>,
    ttfb_duration: f64,
    serve_duration: f64,
    recv_header_bytes: u64,
    recv_body_bytes: u64,
    recv_total_bytes: u64,
    sent_header_bytes: u64,
    sent_body_bytes: u64,
    sent_total_bytes: u64,
    esi_count: usize,
    restart_count: usize,
    restart_log: Option<LogBook<'a>>,
    log: LogBook<'a>,
}

impl<'a> EntryType for ClientAccessLogEntry<'a> {
    fn type_name() -> &'static str {
        "client_access"
    }
    fn remote_ip(&self) -> &str {
        self.remote_address.ip
    }
    fn timestamp(&self) -> f64 {
        self.end_timestamp
    }
    fn request_method(&self) -> &str {
        self.request.method
    }
    fn request_url(&self) -> &str {
        self.request.url
    }
    fn request_protocol(&self) -> &str {
        self.request.protocol
    }
    fn response_status(&self) -> Option<u32> {
        Some(self.response.status)
    }
    fn response_bytes(&self) -> Option<u64> {
        Some(self.sent_total_bytes)
    }
}

#[derive(Serialize, Debug)]
struct BackendAccessLogEntry<'a> {
    record_type: &'a str,
    vxid: u32,
    remote_address: AddressLogEntry<'a>,
    session_timestamp: f64,
    start_timestamp: f64,
    end_timestamp: f64,
    handing: &'a str,
    request: HttpRequestLogEntry<'a>,
    response: Option<HttpResponseLogEntry<'a>>,
    send_duration: f64,
    wait_duration: Option<f64>,
    ttfb_duration: Option<f64>,
    fetch_duration: Option<f64>,
    sent_header_bytes: Option<u64>,
    sent_body_bytes: Option<u64>,
    sent_total_bytes: Option<u64>,
    recv_header_bytes: Option<u64>,
    recv_body_bytes: Option<u64>,
    recv_total_bytes: Option<u64>,
    retry: usize,
    backend_connection: Option<BackendConnectionLogEntry<'a>>,
    cache_object: Option<CacheObjectLogEntry<'a>>,
    log: LogBook<'a>,
}

impl<'a> EntryType for BackendAccessLogEntry<'a> {
    fn type_name() -> &'static str {
        "backend_access"
    }
    fn remote_ip(&self) -> &str {
        self.remote_address.ip
    }
    fn timestamp(&self) -> f64 {
        self.end_timestamp
    }
    fn request_method(&self) -> &str {
        self.request.method
    }
    fn request_url(&self) -> &str {
        self.request.url
    }
    fn request_protocol(&self) -> &str {
        self.request.protocol
    }
    fn response_status(&self) -> Option<u32> {
        self.response.as_ref().map(|r| r.status).or(Some(503)) // no response
    }
    fn response_bytes(&self) -> Option<u64> {
        self.recv_total_bytes
    }
}

#[derive(Serialize, Debug)]
struct PipeSessionLogEntry<'a> {
    record_type: &'a str,
    vxid: u32,
    remote_address: AddressLogEntry<'a>,
    session_timestamp: f64,
    start_timestamp: f64,
    end_timestamp: f64,
    backend_connection: BackendConnectionLogEntry<'a>,
    request: HttpRequestLogEntry<'a>,
    backend_request: HttpRequestLogEntry<'a>,
    process_duration: Option<f64>,
    ttfb_duration: f64,
    recv_total_bytes: u64,
    sent_total_bytes: u64,
    log: LogBook<'a>,
}

impl<'a> EntryType for PipeSessionLogEntry<'a> {
    fn type_name() -> &'static str {
        "pipe_session"
    }
    fn remote_ip(&self) -> &str {
        self.remote_address.ip
    }
    fn timestamp(&self) -> f64 {
        self.end_timestamp
    }
    fn request_method(&self) -> &str {
        self.request.method
    }
    fn request_url(&self) -> &str {
        self.request.url
    }
    fn request_protocol(&self) -> &str {
        self.request.protocol
    }
    fn response_status(&self) -> Option<u32> {
        None
    }
    fn response_bytes(&self) -> Option<u64> {
        Some(self.sent_total_bytes)
    }
}

#[derive(Serialize, Debug)]
struct AddressLogEntry<'a> {
    ip: &'a str,
    port: u16,
}

#[derive(Debug)]
struct HeadersLogEntry<'a> {
    headers: &'a [(String, String)],
}

impl<'a> Serialize for HeadersLogEntry<'a> {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
        let mut state = try!(serializer.serialize_tuple(self.headers.len()));
        for &(ref name, ref value) in self.headers {
            try!(serializer.serialize_tuple_elt(&mut state, (name.as_str(), value.as_str())));
        }
        try!(serializer.serialize_tuple_end(state));
        Ok(())
    }
}

#[derive(Serialize, Debug)]
struct HttpRequestLogEntry<'a> {
    protocol: &'a str,
    method: &'a str,
    url: &'a str,
    headers: HeadersLogEntry<'a>,
}

#[derive(Serialize, Debug)]
struct HttpResponseLogEntry<'a> {
    status: u32,
    reason: &'a str,
    protocol: &'a str,
    headers: HeadersLogEntry<'a>,
}

#[derive(Debug)]
struct LogBook<'a> {
    entries: &'a [LogEntry],
}

#[derive(Serialize, Debug)]
struct LogBookEntry<'a> {
    entry_type: &'a str,
    message: &'a str,
    #[serde(skip_serializing_if="Option::is_none")]
    detail: Option<&'a str>,
}

impl<'a> Serialize for LogBook<'a> {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
        let mut state = try!(serializer.serialize_seq(Some(self.entries.len())));
        for log_entry in self.entries {
            let (entry_type, message, detail) = match log_entry {
                &LogEntry::Vcl(ref msg) => ("VCL", msg.as_str(), None),
                &LogEntry::Debug(ref msg) => ("Debug", msg.as_str(), None),
                &LogEntry::Error(ref msg) => ("Error", msg.as_str(), None),
                &LogEntry::FetchError(ref msg) => ("Fetch Error", msg.as_str(), None),
                &LogEntry::Warning(ref msg) => ("Warning", msg.as_str(), None),
                &LogEntry::Acl(ref result, ref name, ref addr) => match result.as_str() {
                    "MATCH" => ("ACL Match", name.as_str(), addr.as_ref().map(String::as_str)),
                    "NO_MATCH" => ("ACL No Match", name.as_str(), addr.as_ref().map(String::as_str)),
                    _ => ("ACL Other", result.as_str(), Some(name.as_str())),
                },
            };

            try!(serializer.serialize_seq_elt(&mut state, &LogBookEntry {
                entry_type: entry_type,
                message: message,
                detail: detail,
            }));
        }
        try!(serializer.serialize_seq_end(state));
        Ok(())
    }
}

#[derive(Serialize, Debug)]
struct CacheObjectLogEntry<'a> {
    storage_type: &'a str,
    storage_name: &'a str,
    ttl_duration: Option<f64>,
    grace_duration: Option<f64>,
    keep_duration: Option<f64>,
    since_timestamp: f64,
    origin_timestamp: f64,
    fetch_mode: &'a str,
    fetch_streamed: bool,
    response: HttpResponseLogEntry<'a>,
}

#[derive(Serialize, Debug)]
struct BackendConnectionLogEntry<'a> {
    fd: isize,
    name: &'a str,
    remote_address: AddressLogEntry<'a>,
    local_address: AddressLogEntry<'a>,
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::fs::File;
use std::process::{Command, Stdio};

extern crate clap;
use clap::{Arg, App};

extern crate isbfc;
use isbfc::Token::*;
use isbfc::parse;
use isbfc::optimize;

fn main() {
    let matches = App::new("isbfc")
        .version("0.0.1")
        .author("Ian D. Scott <ian@iandouglasscott.com>")
        .about("Brainfuck compiler")
        .arg(Arg::with_name("output_asm")
             .short("S")
             .help("Assemble but do not link"))
        .arg(Arg::with_name("tape-size")
             .long("tape-size")
             .help("Size of tape; defaults to 8192")
             .takes_value(true)
             .empty_values(false))
        .arg(Arg::with_name("FILENAME")
             .help("Source file to compile")
             .required(true)
             .index(1))
        .get_matches();

    let mut tape_size = 8192;
    if let Some(tape_size_str) = matches.value_of("tape-size") {
        tape_size = tape_size_str.parse::<i32>().unwrap();
    }

    let path = matches.value_of("FILENAME").unwrap();
    let name = path.rsplitn(2, '.').last().unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    println!("Compiling...");
    let output = compile(&code, tape_size);
    if matches.is_present("output_asm") {
        let mut asmfile = File::create(format!("{}.s", name)).unwrap();
        asmfile.write_all(&output.into_bytes()).unwrap();
    } else {
        asm_and_link(&output, &name);
    }
}

fn compile(code: &str, tape_size: i32) -> String {
    let tokens = parse(code);
    let tokens = optimize(tokens);

    let mut output = String::new();
    let mut loops: Vec<i32> = Vec::new();
    let mut ifs: Vec<i32> = Vec::new();
    let mut loopnum = 0;
    let mut ifnum = 0;
    let mut outbuffpos = 0;
    let mut outbuffsize = 0;
    for token in tokens.iter() {
        match *token {
            Add(offset, value) => {
                let dest = if offset == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (offset*8))
                };
                if value == 1 && dest == "%r12" {
                    output.push_str("    inc %r12\n");
                } else if value >= 1 {
                    output.push_str(&format!("    addq ${}, {}\n", value, dest));
                } else if value == -1 && dest == "%r12" {
                    output.push_str("    dec %r12\n");
                } else if value <= -1 {
                    output.push_str(&format!("    subq ${}, {}\n", -value, dest));
                }
            },
            MulCopy(src_idx, dest_idx, mul) => {
                let mut src = if src_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (src_idx*8))
                };
                let dest = if dest_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (dest_idx*8))
                };

                if mul != -1 && mul != 1 {
                    output.push_str(&format!(concat!(
                                "    movq {}, %rax\n",
                                "    movq ${}, %rdx\n",
                                "    mulq %rdx\n"), src, mul.abs()));
                    src = "%rax".to_string();
                } else if src != "%r12" && dest != "%r12" {
                    // x86 cannot move memory to memory
                    output.push_str(&format!("    movq {}, %rax\n", src));
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    output.push_str(&format!("    addq {}, {}\n", src, dest));
                } else {
                    output.push_str(&format!("    subq {}, {}\n", src, dest));
                }
            },
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    output.push_str("    xor %r12, %r12\n");
                } else if offset == 0 {
                    output.push_str(&format!("    movq ${}, %r12\n", value));
                } else {
                    output.push_str(&format!("    movq ${}, {}(%rbx)\n", value, offset*8));
                }
            },
            Move(offset) => {
                if offset != 0 {
                    output.push_str("    movq %r12, (%rbx)\n");
                    if offset > 0 {
                        output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                    } else {
                        output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                    }
                    output.push_str("    movq (%rbx), %r12\n");
                }
            },
            Loop => {
                loopnum += 1;
                loops.push(loopnum);
                output.push_str(&format!(concat!(
                        "    jmp endloop{}\n",
                        "    loop{}:\n"),
                        loopnum, loopnum));
            },
            EndLoop => {
                let curloop = loops.pop().unwrap();
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    test %r12, %r12\n",
                            "    jnz loop{}\n"),
                            curloop, curloop))
            },
            If(offset) => {
                ifnum += 1;
                ifs.push(ifnum);
                if offset == 0 {
                    output.push_str("    test %r12, %r12\n");
                } else {
                    output.push_str(&format!("    cmpq $0, {}(%rbx)\n", offset*8));
                }
                output.push_str(&format!("    jz endif{}\n", ifnum));
            },
            EndIf =>
                output.push_str(&format!("    endif{}:\n", ifs.pop().unwrap())),
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                loopnum += 1;
                output.push_str(&format!(concat!(
                            "    movq %r12, (%rbx)\n",
                            "    jmp endloop{}\n",
                            "    loop{}:\n"),
                            loopnum, loopnum));
                if offset > 0 {
                    output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                } else {
                    output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                }
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    cmp $0, (%rbx)\n",
                            "    jnz loop{}\n",
                            "    movq (%rbx), %r12\n"),
                            loopnum, loopnum));
            },
            Input =>
                output.push_str(concat!("\n    xor %rax, %rax\n",
                                        "    xor %rdi, %rdi\n",
                                        "    movq %rbx, %rsi\n",
                                        "    movq $1, %rdx\n",
                                        "    syscall\n",
                                        "    movq (%rbx), %r12\n\n")),
            LoadOut(offset, add) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                if offset == 0 {
                    output.push_str(&format!("    movq %r12, {}\n", outaddr));
                } else {
                    output.push_str(&format!("    movq {}(%rbx), %rax\n", offset*8));
                    output.push_str(&format!("    movq %rax, {}\n", outaddr));
                }
                if add > 0 {
                    output.push_str(&format!("    addb ${}, {}\n", add, outaddr));
                } else if add < 0 {
                    output.push_str(&format!("    subb ${}, {}\n", -add, outaddr));
                }
                outbuffpos += 1;
            },
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                output.push_str(&format!("    movq ${}, {}\n", value, outaddr));
                outbuffpos += 1;
            },
            Output => {
                output.push_str(&format!(concat!(
                            "    movq $1, %rax\n",
                            "    movq $1, %rdi\n",
                            "    movq $strbuff, %rsi\n",
                            "    movq ${}, %rdx\n",
                            "    syscall\n\n"),
                            outbuffpos));

                if outbuffsize < outbuffpos + 8 {
                    outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }

    // Exit syscall
    output.push_str(concat!("\n    movq $60, %rax\n",
                            "    movq $0, %rdi\n",
                            "    syscall\n"));

    format!(concat!(
            ".section .bss\n",
            "    .lcomm strbuff, {outbuffsize}\n",
            "    .lcomm mem, {}\n",
            "    .set startidx, mem + {}\n",
            ".section .text\n",
            ".global _start\n",
            "_start:\n",
            "    xor %r12, %r12\n",
            "    movq $startidx, %rbx\n\n{}"),
            tape_size, tape_size/2, output, outbuffsize=outbuffsize)
}

fn asm_and_link(code: &str, name: &str) {;
    println!("Assembling...");

    let mut child = Command::new("as")
        .arg("-g")
        .arg("-") // Standard input
        .arg("-o")
        .arg(format!("{}.o", name))
        .stdin(Stdio::piped())
        .spawn().unwrap();

    child.stdin.take().unwrap().write_all(code.as_bytes()).unwrap();

    let status = child.wait().unwrap();
    if status.code() == Some(0) {
        println!("Linking...");
        Command::new("ld")
            .arg(format!("{}.o", name))
            .arg("-o")
            .arg(name)
            .spawn().unwrap();
    }
}
<commit_msg>Add -o argument<commit_after>use std::io::prelude::*;
use std::fs::File;
use std::process::{Command, Stdio};

extern crate clap;
use clap::{Arg, App};

extern crate isbfc;
use isbfc::Token::*;
use isbfc::parse;
use isbfc::optimize;

fn main() {
    let matches = App::new("isbfc")
        .version("0.0.1")
        .author("Ian D. Scott <ian@iandouglasscott.com>")
        .about("Brainfuck compiler")
        .arg(Arg::with_name("output_asm")
             .short("S")
             .help("Assemble but do not link"))
        .arg(Arg::with_name("out_name")
             .short("o")
             .help("Output file name")
             .takes_value(true))
        .arg(Arg::with_name("tape_size")
             .long("tape-size")
             .help("Size of tape; defaults to 8192")
             .takes_value(true)
             .empty_values(false))
        .arg(Arg::with_name("FILENAME")
             .help("Source file to compile")
             .required(true)
             .index(1))
        .get_matches();

    let mut tape_size = 8192;
    if let Some(tape_size_str) = matches.value_of("tape_size") {
        tape_size = tape_size_str.parse::<i32>().unwrap();
    }

    let path = matches.value_of("FILENAME").unwrap();
    let name = path.rsplitn(2, '.').last().unwrap();
    let out_name = matches.value_of("out_name").unwrap_or(name);
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    println!("Compiling...");
    let output = compile(&code, tape_size);
    if matches.is_present("output_asm") {
        let mut asmfile = File::create(format!("{}.s", name)).unwrap();
        asmfile.write_all(&output.into_bytes()).unwrap();
    } else {
        asm_and_link(&output, &name, &out_name);
    }
}

fn compile(code: &str, tape_size: i32) -> String {
    let tokens = parse(code);
    let tokens = optimize(tokens);

    let mut output = String::new();
    let mut loops: Vec<i32> = Vec::new();
    let mut ifs: Vec<i32> = Vec::new();
    let mut loopnum = 0;
    let mut ifnum = 0;
    let mut outbuffpos = 0;
    let mut outbuffsize = 0;
    for token in tokens.iter() {
        match *token {
            Add(offset, value) => {
                let dest = if offset == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (offset*8))
                };
                if value == 1 && dest == "%r12" {
                    output.push_str("    inc %r12\n");
                } else if value >= 1 {
                    output.push_str(&format!("    addq ${}, {}\n", value, dest));
                } else if value == -1 && dest == "%r12" {
                    output.push_str("    dec %r12\n");
                } else if value <= -1 {
                    output.push_str(&format!("    subq ${}, {}\n", -value, dest));
                }
            },
            MulCopy(src_idx, dest_idx, mul) => {
                let mut src = if src_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (src_idx*8))
                };
                let dest = if dest_idx == 0 {
                    "%r12".to_string()
                } else {
                    format!("{}(%rbx)", (dest_idx*8))
                };

                if mul != -1 && mul != 1 {
                    output.push_str(&format!(concat!(
                                "    movq {}, %rax\n",
                                "    movq ${}, %rdx\n",
                                "    mulq %rdx\n"), src, mul.abs()));
                    src = "%rax".to_string();
                } else if src != "%r12" && dest != "%r12" {
                    // x86 cannot move memory to memory
                    output.push_str(&format!("    movq {}, %rax\n", src));
                    src = "%rax".to_string();
                }

                if mul > 0 {
                    output.push_str(&format!("    addq {}, {}\n", src, dest));
                } else {
                    output.push_str(&format!("    subq {}, {}\n", src, dest));
                }
            },
            Set(offset, value) => {
                if offset == 0 && value == 0 {
                    output.push_str("    xor %r12, %r12\n");
                } else if offset == 0 {
                    output.push_str(&format!("    movq ${}, %r12\n", value));
                } else {
                    output.push_str(&format!("    movq ${}, {}(%rbx)\n", value, offset*8));
                }
            },
            Move(offset) => {
                if offset != 0 {
                    output.push_str("    movq %r12, (%rbx)\n");
                    if offset > 0 {
                        output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                    } else {
                        output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                    }
                    output.push_str("    movq (%rbx), %r12\n");
                }
            },
            Loop => {
                loopnum += 1;
                loops.push(loopnum);
                output.push_str(&format!(concat!(
                        "    jmp endloop{}\n",
                        "    loop{}:\n"),
                        loopnum, loopnum));
            },
            EndLoop => {
                let curloop = loops.pop().unwrap();
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    test %r12, %r12\n",
                            "    jnz loop{}\n"),
                            curloop, curloop))
            },
            If(offset) => {
                ifnum += 1;
                ifs.push(ifnum);
                if offset == 0 {
                    output.push_str("    test %r12, %r12\n");
                } else {
                    output.push_str(&format!("    cmpq $0, {}(%rbx)\n", offset*8));
                }
                output.push_str(&format!("    jz endif{}\n", ifnum));
            },
            EndIf =>
                output.push_str(&format!("    endif{}:\n", ifs.pop().unwrap())),
            Scan(offset) => {
                // Slighly more optimal than normal loop and move
                loopnum += 1;
                output.push_str(&format!(concat!(
                            "    movq %r12, (%rbx)\n",
                            "    jmp endloop{}\n",
                            "    loop{}:\n"),
                            loopnum, loopnum));
                if offset > 0 {
                    output.push_str(&format!("    addq ${}, %rbx\n", offset*8));
                } else {
                    output.push_str(&format!("    subq ${}, %rbx\n", -offset*8));
                }
                output.push_str(&format!(concat!(
                            "    endloop{}:\n",
                            "    cmp $0, (%rbx)\n",
                            "    jnz loop{}\n",
                            "    movq (%rbx), %r12\n"),
                            loopnum, loopnum));
            },
            Input =>
                output.push_str(concat!("\n    xor %rax, %rax\n",
                                        "    xor %rdi, %rdi\n",
                                        "    movq %rbx, %rsi\n",
                                        "    movq $1, %rdx\n",
                                        "    syscall\n",
                                        "    movq (%rbx), %r12\n\n")),
            LoadOut(offset, add) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                if offset == 0 {
                    output.push_str(&format!("    movq %r12, {}\n", outaddr));
                } else {
                    output.push_str(&format!("    movq {}(%rbx), %rax\n", offset*8));
                    output.push_str(&format!("    movq %rax, {}\n", outaddr));
                }
                if add > 0 {
                    output.push_str(&format!("    addb ${}, {}\n", add, outaddr));
                } else if add < 0 {
                    output.push_str(&format!("    subb ${}, {}\n", -add, outaddr));
                }
                outbuffpos += 1;
            },
            LoadOutSet(value) => {
                let outaddr = format!("(strbuff+{})", outbuffpos);
                output.push_str(&format!("    movq ${}, {}\n", value, outaddr));
                outbuffpos += 1;
            },
            Output => {
                output.push_str(&format!(concat!(
                            "    movq $1, %rax\n",
                            "    movq $1, %rdi\n",
                            "    movq $strbuff, %rsi\n",
                            "    movq ${}, %rdx\n",
                            "    syscall\n\n"),
                            outbuffpos));

                if outbuffsize < outbuffpos + 8 {
                    outbuffsize = outbuffpos + 8;
                }
                outbuffpos = 0;
            }
        }
    }

    // Exit syscall
    output.push_str(concat!("\n    movq $60, %rax\n",
                            "    movq $0, %rdi\n",
                            "    syscall\n"));

    format!(concat!(
            ".section .bss\n",
            "    .lcomm strbuff, {outbuffsize}\n",
            "    .lcomm mem, {}\n",
            "    .set startidx, mem + {}\n",
            ".section .text\n",
            ".global _start\n",
            "_start:\n",
            "    xor %r12, %r12\n",
            "    movq $startidx, %rbx\n\n{}"),
            tape_size, tape_size/2, output, outbuffsize=outbuffsize)
}

fn asm_and_link(code: &str, name: &str, out_name: &str) {;
    println!("Assembling...");

    let mut child = Command::new("as")
        .arg("-g")
        .arg("-") // Standard input
        .arg("-o")
        .arg(format!("{}.o", name))
        .stdin(Stdio::piped())
        .spawn().unwrap();

    child.stdin.take().unwrap().write_all(code.as_bytes()).unwrap();

    let status = child.wait().unwrap();
    if status.code() == Some(0) {
        println!("Linking...");
        Command::new("ld")
            .arg(format!("{}.o", name))
            .arg("-o")
            .arg(out_name)
            .spawn().unwrap();
    }
}
<|endoftext|>


<commit_before>#![warn(clippy::all, rust_2018_idioms)]
#![allow(unknown_lints)]

use cargo_registry::{boot, App, Env};
use std::{
    borrow::Cow,
    fs::File,
    sync::{mpsc::channel, Arc, Mutex},
    thread,
    time::Duration,
};

use civet::Server as CivetServer;
use conduit_hyper::Service;
use futures_util::future::FutureExt;
use reqwest::blocking::Client;
use sentry::{ClientOptions, IntoDsn};

const CORE_THREADS: usize = 4;

#[allow(clippy::large_enum_variant)]
enum Server {
    Civet(CivetServer),
    Hyper(tokio::runtime::Runtime, tokio::task::JoinHandle<()>),
}

use Server::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let _sentry = dotenv::var("SENTRY_DSN_API")
        .ok()
        .into_dsn()
        .expect("SENTRY_DSN_API is not a valid Sentry DSN value")
        .map(|dsn| {
            let mut opts = ClientOptions::from(dsn);
            opts.environment = Some(
                dotenv::var("SENTRY_ENV_API")
                    .map(Cow::Owned)
                    .expect("SENTRY_ENV_API must be set when using SENTRY_DSN_API"),
            );

            opts.release = dotenv::var("HEROKU_SLUG_COMMIT").ok().map(Into::into);

            sentry::init(opts)
        });

    // Initialize logging
    tracing_subscriber::fmt::init();

    let config = cargo_registry::Config::default();
    let client = Client::new();
    let app = Arc::new(App::new(config.clone(), Some(client)));

    // Start the background thread periodically persisting download counts to the database.
    downloads_counter_thread(app.clone());

    let app = cargo_registry::build_handler(app);

    // On every server restart, ensure the categories available in the database match
    // the information in *src/categories.toml*.
    let categories_toml = include_str!("../boot/categories.toml");
    boot::categories::sync(categories_toml).unwrap();

    let heroku = dotenv::var("HEROKU").is_ok();
    let fastboot = dotenv::var("USE_FASTBOOT").is_ok();
    let dev_docker = dotenv::var("DEV_DOCKER").is_ok();

    let ip = if dev_docker {
        [0, 0, 0, 0]
    } else {
        [127, 0, 0, 1]
    };
    let port = if heroku {
        8888
    } else {
        dotenv::var("PORT")
            .ok()
            .and_then(|s| s.parse().ok())
            .unwrap_or(8888)
    };
    let threads = dotenv::var("SERVER_THREADS")
        .map(|s| s.parse().expect("SERVER_THREADS was not a valid number"))
        .unwrap_or_else(|_| {
            if config.env == Env::Development {
                5
            } else {
                // A large default because this can be easily changed via env and in production we
                // want the logging middleware to accurately record the start time.
                500
            }
        });

    let server = if dotenv::var("WEB_USE_CIVET").is_err() {
        use tokio::io::AsyncWriteExt;
        use tokio::signal::unix::{signal, SignalKind};

        println!("Booting with a hyper based server");

        let rt = tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .worker_threads(CORE_THREADS)
            .max_blocking_threads(threads as usize)
            .build()
            .unwrap();

        let handler = Arc::new(conduit_hyper::BlockingHandler::new(app));
        let make_service =
            hyper::service::make_service_fn(move |socket: &hyper::server::conn::AddrStream| {
                let addr = socket.remote_addr();
                let handler = handler.clone();
                async move { Service::from_blocking(handler, addr) }
            });

        let addr = (ip, port).into();
        #[allow(clippy::async_yields_async)]
        let server = rt.block_on(async { hyper::Server::bind(&addr).serve(make_service) });

        let mut sig_int = rt.block_on(async { signal(SignalKind::interrupt()) })?;
        let mut sig_term = rt.block_on(async { signal(SignalKind::terminate()) })?;

        let server = server.with_graceful_shutdown(async move {
            // Wait for either signal
            futures_util::select! {
                _ = sig_int.recv().fuse() => {},
                _ = sig_term.recv().fuse() => {},
            };
            let mut stdout = tokio::io::stdout();
            stdout.write_all(b"Starting graceful shutdown\n").await.ok();
        });

        let server = rt.spawn(async { server.await.unwrap() });
        Hyper(rt, server)
    } else {
        println!("Booting with a civet based server");
        let mut cfg = civet::Config::new();
        cfg.port(port).threads(threads).keep_alive(true);
        Civet(CivetServer::start(cfg, app).unwrap())
    };

    println!("listening on port {}", port);

    // Give tokio a chance to spawn the first worker thread
    thread::sleep(Duration::from_millis(10));

    // Creating this file tells heroku to tell nginx that the application is ready
    // to receive traffic.
    if heroku {
        let path = if fastboot {
            "/tmp/backend-initialized"
        } else {
            "/tmp/app-initialized"
        };
        println!("Writing to {}", path);
        File::create(path).unwrap();
    }

    // Block the main thread until the server has shutdown
    match server {
        Hyper(rt, server) => {
            rt.block_on(async { server.await.unwrap() });
        }
        Civet(server) => {
            let (tx, rx) = channel::<()>();
            ctrlc_handler(move || tx.send(()).unwrap_or(()));
            rx.recv().unwrap();
            drop(server);
        }
    }

    println!("Server has gracefully shutdown!");
    Ok(())
}

fn ctrlc_handler<F>(f: F)
where
    F: FnOnce() + Send + 'static,
{
    let call_once = Mutex::new(Some(f));

    ctrlc::set_handler(move || {
        if let Some(f) = call_once.lock().unwrap().take() {
            println!("Starting graceful shutdown");
            f();
        } else {
            println!("Already sent signal to start graceful shutdown");
        }
    })
    .unwrap();
}

fn downloads_counter_thread(app: Arc<App>) {
    let interval = Duration::from_millis(
        (app.config.downloads_persist_interval_ms / app.downloads_counter.shards_count()) as u64,
    );

    std::thread::spawn(move || loop {
        std::thread::sleep(interval);

        if let Err(err) = app.downloads_counter.persist_next_shard(&app) {
            println!("downloads_counter error: {}", err);
        }
    });
}
<commit_msg>server: avoid shadowing the app<commit_after>#![warn(clippy::all, rust_2018_idioms)]
#![allow(unknown_lints)]

use cargo_registry::{boot, App, Env};
use std::{
    borrow::Cow,
    fs::File,
    sync::{mpsc::channel, Arc, Mutex},
    thread,
    time::Duration,
};

use civet::Server as CivetServer;
use conduit_hyper::Service;
use futures_util::future::FutureExt;
use reqwest::blocking::Client;
use sentry::{ClientOptions, IntoDsn};

const CORE_THREADS: usize = 4;

#[allow(clippy::large_enum_variant)]
enum Server {
    Civet(CivetServer),
    Hyper(tokio::runtime::Runtime, tokio::task::JoinHandle<()>),
}

use Server::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let _sentry = dotenv::var("SENTRY_DSN_API")
        .ok()
        .into_dsn()
        .expect("SENTRY_DSN_API is not a valid Sentry DSN value")
        .map(|dsn| {
            let mut opts = ClientOptions::from(dsn);
            opts.environment = Some(
                dotenv::var("SENTRY_ENV_API")
                    .map(Cow::Owned)
                    .expect("SENTRY_ENV_API must be set when using SENTRY_DSN_API"),
            );

            opts.release = dotenv::var("HEROKU_SLUG_COMMIT").ok().map(Into::into);

            sentry::init(opts)
        });

    // Initialize logging
    tracing_subscriber::fmt::init();

    let config = cargo_registry::Config::default();
    let client = Client::new();
    let app = Arc::new(App::new(config.clone(), Some(client)));

    // Start the background thread periodically persisting download counts to the database.
    downloads_counter_thread(app.clone());

    let handler = cargo_registry::build_handler(app);

    // On every server restart, ensure the categories available in the database match
    // the information in *src/categories.toml*.
    let categories_toml = include_str!("../boot/categories.toml");
    boot::categories::sync(categories_toml).unwrap();

    let heroku = dotenv::var("HEROKU").is_ok();
    let fastboot = dotenv::var("USE_FASTBOOT").is_ok();
    let dev_docker = dotenv::var("DEV_DOCKER").is_ok();

    let ip = if dev_docker {
        [0, 0, 0, 0]
    } else {
        [127, 0, 0, 1]
    };
    let port = if heroku {
        8888
    } else {
        dotenv::var("PORT")
            .ok()
            .and_then(|s| s.parse().ok())
            .unwrap_or(8888)
    };
    let threads = dotenv::var("SERVER_THREADS")
        .map(|s| s.parse().expect("SERVER_THREADS was not a valid number"))
        .unwrap_or_else(|_| {
            if config.env == Env::Development {
                5
            } else {
                // A large default because this can be easily changed via env and in production we
                // want the logging middleware to accurately record the start time.
                500
            }
        });

    let server = if dotenv::var("WEB_USE_CIVET").is_err() {
        use tokio::io::AsyncWriteExt;
        use tokio::signal::unix::{signal, SignalKind};

        println!("Booting with a hyper based server");

        let rt = tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .worker_threads(CORE_THREADS)
            .max_blocking_threads(threads as usize)
            .build()
            .unwrap();

        let handler = Arc::new(conduit_hyper::BlockingHandler::new(handler));
        let make_service =
            hyper::service::make_service_fn(move |socket: &hyper::server::conn::AddrStream| {
                let addr = socket.remote_addr();
                let handler = handler.clone();
                async move { Service::from_blocking(handler, addr) }
            });

        let addr = (ip, port).into();
        #[allow(clippy::async_yields_async)]
        let server = rt.block_on(async { hyper::Server::bind(&addr).serve(make_service) });

        let mut sig_int = rt.block_on(async { signal(SignalKind::interrupt()) })?;
        let mut sig_term = rt.block_on(async { signal(SignalKind::terminate()) })?;

        let server = server.with_graceful_shutdown(async move {
            // Wait for either signal
            futures_util::select! {
                _ = sig_int.recv().fuse() => {},
                _ = sig_term.recv().fuse() => {},
            };
            let mut stdout = tokio::io::stdout();
            stdout.write_all(b"Starting graceful shutdown\n").await.ok();
        });

        let server = rt.spawn(async { server.await.unwrap() });
        Hyper(rt, server)
    } else {
        println!("Booting with a civet based server");
        let mut cfg = civet::Config::new();
        cfg.port(port).threads(threads).keep_alive(true);
        Civet(CivetServer::start(cfg, handler).unwrap())
    };

    println!("listening on port {}", port);

    // Give tokio a chance to spawn the first worker thread
    thread::sleep(Duration::from_millis(10));

    // Creating this file tells heroku to tell nginx that the application is ready
    // to receive traffic.
    if heroku {
        let path = if fastboot {
            "/tmp/backend-initialized"
        } else {
            "/tmp/app-initialized"
        };
        println!("Writing to {}", path);
        File::create(path).unwrap();
    }

    // Block the main thread until the server has shutdown
    match server {
        Hyper(rt, server) => {
            rt.block_on(async { server.await.unwrap() });
        }
        Civet(server) => {
            let (tx, rx) = channel::<()>();
            ctrlc_handler(move || tx.send(()).unwrap_or(()));
            rx.recv().unwrap();
            drop(server);
        }
    }

    println!("Server has gracefully shutdown!");
    Ok(())
}

fn ctrlc_handler<F>(f: F)
where
    F: FnOnce() + Send + 'static,
{
    let call_once = Mutex::new(Some(f));

    ctrlc::set_handler(move || {
        if let Some(f) = call_once.lock().unwrap().take() {
            println!("Starting graceful shutdown");
            f();
        } else {
            println!("Already sent signal to start graceful shutdown");
        }
    })
    .unwrap();
}

fn downloads_counter_thread(app: Arc<App>) {
    let interval = Duration::from_millis(
        (app.config.downloads_persist_interval_ms / app.downloads_counter.shards_count()) as u64,
    );

    std::thread::spawn(move || loop {
        std::thread::sleep(interval);

        if let Err(err) = app.downloads_counter.persist_next_shard(&app) {
            println!("downloads_counter error: {}", err);
        }
    });
}
<|endoftext|>


<commit_before>pub mod messages;

use address::Address;
use mio;
use network;
use self::messages::{CommMessage, Message};
use std::collections::HashSet;
use std::io;
use std::sync::mpsc;
use std::thread;

#[derive(Debug)]
pub enum Task {
    HandleNetworkEvent(network::Event),
    ScheduleMessageDelivery(Address, CommMessage)
}

#[derive(Debug)]
pub enum ScheduledTask {
    DeliverMessage(Address, CommMessage),
}

pub struct Client {
    address: Address,
    network_commands: Option<network::TaskSender>,
    received: HashSet<Address>,
    acknowledged: HashSet<Address>
}

impl Client {
    pub fn new(address: Address) -> Client {
        Client {
            address: address,
            network_commands: None,
            received: HashSet::new(),
            acknowledged: HashSet::new()
        }
    }

    pub fn run(mut self, mut network: network::Network) {
        let mut event_loop = mio::EventLoop::new().unwrap();
        let (event_sender, event_receiver) = mpsc::channel();
        network.register_event_listener(event_sender);
        let notify_channel = event_loop.channel();
        let sender = self.address;

        thread::spawn(move|| {
            for event in event_receiver.iter() {
                notify_channel.send(Task::HandleNetworkEvent(event)).unwrap();
            }
        });

        self.network_commands = Some(network.run());

        let notify_channel = event_loop.channel();
        thread::spawn(move || event_loop.run(&mut self).unwrap());

        loop {
            let mut line = String::new();
            io::stdin().read_line(&mut line).unwrap();
            let parts: Vec<&str> = line.splitn(2, ' ').collect();
            let recipient = Address::from_str(parts[0]);
            let message_text = parts[1].trim().to_string();

            let message = messages::create_text_message(recipient, sender, message_text);
            notify_channel.send(Task::ScheduleMessageDelivery(recipient, message)).unwrap();
        }
    }

    fn handle_networking_event(&mut self, event: network::Event, event_loop: &mut mio::EventLoop<Client>) {
        match event {
            network::Event::ReceivedPacket(data) => {
                let comm_message = messages::decode(data);
                let CommMessage { recipient, message } = comm_message.clone();
                match message {
                    Message::TextMessage { id, sender, text } => {
                        if recipient == self.address && !self.received.contains(&id) {
                            println!("{}: {}", sender, text);
                            self.received.insert(id);
                            let ack = messages::create_message_acknowledgement(sender, id);
                            self.schedule_message_delivery(sender, ack, event_loop);
                        } else {
                            if !self.acknowledged.contains(&id) {
                                self.schedule_message_delivery(recipient, comm_message, event_loop);
                            }
                        }
                    }

                    Message::MessageAcknowledgement { id } => {
                        if self.acknowledged.insert(id) {
                            if recipient == self.address {
                                println!("ack {}", id);
                            } else {
                                self.schedule_message_delivery(recipient, comm_message, event_loop);
                            }
                        }
                    }
                }
            }
        }
    }

    fn schedule_message_delivery(&mut self, recipient: Address, message: CommMessage, event_loop: &mut mio::EventLoop<Client>) {
        event_loop.timeout_ms(ScheduledTask::DeliverMessage(recipient, message), 0).unwrap();
    }

    fn deliver_message(&mut self, recipient: Address, message: CommMessage) {
        match self.network_commands {
            Some(ref c) => c.send(network::OneshotTask::SendPacket(recipient, message.encode())).unwrap(),
            None => { println!("No network_commands") }
        }
    }
}

impl mio::Handler for Client {
    type Timeout = ScheduledTask;
    type Message = Task;

    fn notify(&mut self, event_loop: &mut mio::EventLoop<Client>, task: Task) {
        match task {
            Task::HandleNetworkEvent(event) => self.handle_networking_event(event, event_loop),
            Task::ScheduleMessageDelivery(recipient, message) => self.schedule_message_delivery(recipient, message, event_loop)
        }
    }

    fn timeout(&mut self, _event_loop: &mut mio::EventLoop<Client>, task: ScheduledTask) {
        match task {
            ScheduledTask::DeliverMessage(recipient, message) => self.deliver_message(recipient, message)
        }
    }
}
<commit_msg>Print own address at start<commit_after>pub mod messages;

use address::Address;
use mio;
use network;
use self::messages::{CommMessage, Message};
use std::collections::HashSet;
use std::io;
use std::sync::mpsc;
use std::thread;

#[derive(Debug)]
pub enum Task {
    HandleNetworkEvent(network::Event),
    ScheduleMessageDelivery(Address, CommMessage)
}

#[derive(Debug)]
pub enum ScheduledTask {
    DeliverMessage(Address, CommMessage),
}

pub struct Client {
    address: Address,
    network_commands: Option<network::TaskSender>,
    received: HashSet<Address>,
    acknowledged: HashSet<Address>
}

impl Client {
    pub fn new(address: Address) -> Client {
        Client {
            address: address,
            network_commands: None,
            received: HashSet::new(),
            acknowledged: HashSet::new()
        }
    }

    pub fn run(mut self, mut network: network::Network) {
        let mut event_loop = mio::EventLoop::new().unwrap();
        let (event_sender, event_receiver) = mpsc::channel();
        network.register_event_listener(event_sender);
        let notify_channel = event_loop.channel();
        let sender = self.address;

        thread::spawn(move|| {
            for event in event_receiver.iter() {
                notify_channel.send(Task::HandleNetworkEvent(event)).unwrap();
            }
        });

        self.network_commands = Some(network.run());

        let notify_channel = event_loop.channel();
        thread::spawn(move || event_loop.run(&mut self).unwrap());

        println!("Running client at {}", sender);

        loop {
            let mut line = String::new();
            io::stdin().read_line(&mut line).unwrap();
            let parts: Vec<&str> = line.splitn(2, ' ').collect();
            let recipient = Address::from_str(parts[0]);
            let message_text = parts[1].trim().to_string();

            let message = messages::create_text_message(recipient, sender, message_text);
            notify_channel.send(Task::ScheduleMessageDelivery(recipient, message)).unwrap();
        }
    }

    fn handle_networking_event(&mut self, event: network::Event, event_loop: &mut mio::EventLoop<Client>) {
        match event {
            network::Event::ReceivedPacket(data) => {
                let comm_message = messages::decode(data);
                let CommMessage { recipient, message } = comm_message.clone();
                match message {
                    Message::TextMessage { id, sender, text } => {
                        if recipient == self.address && !self.received.contains(&id) {
                            println!("{}: {}", sender, text);
                            self.received.insert(id);
                            let ack = messages::create_message_acknowledgement(sender, id);
                            self.schedule_message_delivery(sender, ack, event_loop);
                        } else {
                            if !self.acknowledged.contains(&id) {
                                self.schedule_message_delivery(recipient, comm_message, event_loop);
                            }
                        }
                    }

                    Message::MessageAcknowledgement { id } => {
                        if self.acknowledged.insert(id) {
                            if recipient == self.address {
                                println!("ack {}", id);
                            } else {
                                self.schedule_message_delivery(recipient, comm_message, event_loop);
                            }
                        }
                    }
                }
            }
        }
    }

    fn schedule_message_delivery(&mut self, recipient: Address, message: CommMessage, event_loop: &mut mio::EventLoop<Client>) {
        event_loop.timeout_ms(ScheduledTask::DeliverMessage(recipient, message), 0).unwrap();
    }

    fn deliver_message(&mut self, recipient: Address, message: CommMessage) {
        match self.network_commands {
            Some(ref c) => c.send(network::OneshotTask::SendPacket(recipient, message.encode())).unwrap(),
            None => { println!("No network_commands") }
        }
    }
}

impl mio::Handler for Client {
    type Timeout = ScheduledTask;
    type Message = Task;

    fn notify(&mut self, event_loop: &mut mio::EventLoop<Client>, task: Task) {
        match task {
            Task::HandleNetworkEvent(event) => self.handle_networking_event(event, event_loop),
            Task::ScheduleMessageDelivery(recipient, message) => self.schedule_message_delivery(recipient, message, event_loop)
        }
    }

    fn timeout(&mut self, _event_loop: &mut mio::EventLoop<Client>, task: ScheduledTask) {
        match task {
            ScheduledTask::DeliverMessage(recipient, message) => self.deliver_message(recipient, message)
        }
    }
}
<|endoftext|>