<commit_before>use std::collections::HashMap;
use std::str::from_utf8_unchecked;
use std::cmp::min;
use std::process;

use term_painter::Color::*;
use term_painter::ToStyle;

use config;
use logger;
use request::{Request, Data, FormItems};
use router::{Router, Route};
use catcher::{self, Catcher};
use outcome::Outcome;
use error::Error;

use http::{Method, StatusCode};
use http::hyper::{HyperRequest, FreshHyperResponse};
use http::hyper::{HyperServer, HyperHandler, HyperSetCookie, header};

pub struct Rocket {
    address: String,
    port: usize,
    router: Router,
    default_catchers: HashMap<u16, Catcher>,
    catchers: HashMap<u16, Catcher>,
}

#[doc(hidden)]
impl HyperHandler for Rocket {
    fn handle<'h, 'k>(&self,
                      req: HyperRequest<'h, 'k>,
                      mut res: FreshHyperResponse<'h>) {
        res.headers_mut().set(header::Server("rocket".to_string()));
        self.dispatch(req, res)
    }
}

impl Rocket {
    fn dispatch<'h, 'k>(&self,
                        hyp_req: HyperRequest<'h, 'k>,
                        mut res: FreshHyperResponse<'h>) {
        // Get a copy of the URI for later use.
        let uri = hyp_req.uri.to_string();

        // Get all of the information from Hyper.
        let (_, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct();

        // Try to create a Rocket request from the hyper request info.
        let mut request = match Request::new(h_method, h_headers, h_uri) {
            Ok(req) => req,
            Err(ref reason) => {
                let mock_request = Request::mock(Method::Get, uri.as_str());
                debug_!("Bad request: {}", reason);
                return self.handle_error(StatusCode::InternalServerError,
                                         &mock_request, res);
            }
        };

        // Retrieve the data from the request.
        let mut data = match Data::from_hyp(h_body) {
            Ok(data) => data,
            Err(reason) => {
                debug_!("Bad data in request: {}", reason);
                return self.handle_error(StatusCode::InternalServerError,
                                         &request, res);
            }
        };

        // Preprocess the request.
        self.preprocess_request(&mut request, &data);

        info!("{}:", request);
        trace_!("Peek size: {} bytes", data.peek().len());
        let matches = self.router.route(&request);
        for route in matches {
            // Retrieve and set the requests parameters.
            info_!("Matched: {}", route);
            request.set_params(route);

            // Dispatch the request to the handler.
            let response = (route.handler)(&request, data);

            // Check if the request processing completed or if the request needs
            // to be forwarded. If it does, continue the loop to try again.
            info_!("{} {}", White.paint("Response:"), response);
            let mut responder = match response {
                Outcome::Success(responder) => responder,
                Outcome::Failure(status_code) => {
                    return self.handle_error(status_code, &request, res);
                }
                Outcome::Forward(unused_data) => {
                    data = unused_data;
                    continue;
                }
            };

            // We have a responder. Update the cookies in the header.
            let cookie_delta = request.cookies().delta();
            if cookie_delta.len() > 0 {
                res.headers_mut().set(HyperSetCookie(cookie_delta));
            }

            // Actually process the response.
            let outcome = responder.respond(res);
            info_!("{} {}", White.paint("Outcome:"), outcome);

            // Check if the responder wants to forward to a catcher.
            match outcome.forwarded() {
                Some((c, r)) => return self.handle_error(c, &request, r),
                None => return
            };
        }

        error_!("No matching routes.");
        self.handle_error(StatusCode::NotFound, &request, res);
    }

    /// Preprocess the request for Rocket-specific things. At this time, we're
    /// only checking for _method in forms.
    fn preprocess_request(&self, req: &mut Request, data: &Data) {
        // Check if this is a form and if the form contains the special _method
        // field which we use to reinterpret the request's method.
        let data_len = data.peek().len();
        let (min_len, max_len) = ("_method=get".len(), "_method=delete".len());
        if req.content_type().is_form() && data_len >= min_len {
            let form = unsafe {
                from_utf8_unchecked(&data.peek()[..min(data_len, max_len)])
            };

            let mut form_items = FormItems(form);
            if let Some(("_method", value)) = form_items.next() {
                if let Ok(method) = value.parse() {
                    req.method = method;
                }
            }
        }
    }

    // Call when no route was found.
    fn handle_error<'r>(&self,
                        code: StatusCode,
                        req: &'r Request,
                        response: FreshHyperResponse) {

        // Find the catcher or use the one for internal server errors.
        let catcher = self.catchers.get(&code.to_u16()).unwrap_or_else(|| {
            error_!("No catcher found for {}.", code);
            warn_!("Using internal server error catcher.");
            self.catchers.get(&500).expect("500 Catcher")
        });

        if let Some(mut responder) = catcher.handle(Error::NoRoute, req).responder() {
            if !responder.respond(response).is_success() {
                error_!("Catcher outcome was unsuccessul; aborting response.");
            } else {
                info_!("Responded with {} catcher.", White.paint(code));
            }
        } else {
            error_!("Catcher returned an incomplete response.");
            warn_!("Using default error response.");
            let catcher = self.default_catchers.get(&code.to_u16())
                .unwrap_or(self.default_catchers.get(&500).expect("500 default"));
            let responder = catcher.handle(Error::Internal, req).responder();
            responder.unwrap().respond(response).unwrap()
        }<commit_msg>Seperate request dispatch into hyper and non-hyper parts.<commit_after>use std::collections::HashMap;
use std::str::from_utf8_unchecked;
use std::cmp::min;
use std::process;

use term_painter::Color::*;
use term_painter::ToStyle;

use config;
use logger;
use request::{Request, Data, FormItems};
use response::Responder;
use router::{Router, Route};
use catcher::{self, Catcher};
use outcome::Outcome;
use error::Error;

use http::{Method, StatusCode};
use http::hyper::{HyperRequest, FreshHyperResponse};
use http::hyper::{HyperServer, HyperHandler, HyperSetCookie, header};

pub struct Rocket {
    address: String,
    port: usize,
    router: Router,
    default_catchers: HashMap<u16, Catcher>,
    catchers: HashMap<u16, Catcher>,
}

#[doc(hidden)]
impl HyperHandler for Rocket {
    fn handle<'h, 'k>(&self,
                      hyp_req: HyperRequest<'h, 'k>,
                      mut res: FreshHyperResponse<'h>) {
        // Get all of the information from Hyper.
        let (_, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct();

        // Get a copy of the URI for later use.
        let uri = h_uri.to_string();

        // Try to create a Rocket request from the hyper request info.
        let mut request = match Request::new(h_method, h_headers, h_uri) {
            Ok(req) => req,
            Err(ref reason) => {
                let mock_request = Request::mock(Method::Get, uri.as_str());
                error!("{}: bad request ({}).", mock_request, reason);
                return self.handle_error(StatusCode::InternalServerError,
                                         &mock_request, res);
            }
        };

        // Retrieve the data from the hyper body.
        let data = match Data::from_hyp(h_body) {
            Ok(data) => data,
            Err(reason) => {
                error_!("Bad data in request: {}", reason);
                return self.handle_error(StatusCode::InternalServerError,
                                         &request, res);
            }
        };

        // Set the common response headers and preprocess the request.
        res.headers_mut().set(header::Server("rocket".to_string()));
        self.preprocess_request(&mut request, &data);

        // Now that we've Rocket-ized everything, actually dispath the request.
        info!("{}:", request);
        let mut responder = match self.dispatch(&request, data) {
            Ok(responder) => responder,
            Err(code) => return self.handle_error(code, &request, res)
        };

        // We have a responder. Update the cookies in the header.
        let cookie_delta = request.cookies().delta();
        if cookie_delta.len() > 0 {
            res.headers_mut().set(HyperSetCookie(cookie_delta));
        }

        // Actually call the responder.
        let outcome = responder.respond(res);
        info_!("{} {}", White.paint("Outcome:"), outcome);

        // Check if the responder wants to forward to a catcher. If it doesn't,
        // it's a success or failure, so we can't do any more processing.
        if let Some((code, f_res)) = outcome.forwarded() {
            return self.handle_error(code, &request, f_res);
        }
    }
}

impl Rocket {
    fn dispatch<'r>(&self, request: &'r Request, mut data: Data)
            -> Result<Box<Responder + 'r>, StatusCode> {
        // Go through the list of matching routes until we fail or succeed.
        let matches = self.router.route(&request);
        for route in matches {
            // Retrieve and set the requests parameters.
            info_!("Matched: {}", route);
            request.set_params(route);

            // Dispatch the request to the handler.
            let response = (route.handler)(&request, data);

            // Check if the request processing completed or if the request needs
            // to be forwarded. If it does, continue the loop to try again.
            info_!("{} {}", White.paint("Response:"), response);
            match response {
                Outcome::Success(responder) => return Ok(responder),
                Outcome::Failure(status_code) => return Err(status_code),
                Outcome::Forward(unused_data) => data = unused_data,
            };
        }

        error_!("No matching routes.");
        Err(StatusCode::NotFound)
    }

    /// Preprocess the request for Rocket-specific things. At this time, we're
    /// only checking for _method in forms.
    fn preprocess_request(&self, req: &mut Request, data: &Data) {
        // Check if this is a form and if the form contains the special _method
        // field which we use to reinterpret the request's method.
        let data_len = data.peek().len();
        let (min_len, max_len) = ("_method=get".len(), "_method=delete".len());
        if req.content_type().is_form() && data_len >= min_len {
            let form = unsafe {
                from_utf8_unchecked(&data.peek()[..min(data_len, max_len)])
            };

            let mut form_items = FormItems(form);
            if let Some(("_method", value)) = form_items.next() {
                if let Ok(method) = value.parse() {
                    req.method = method;
                }
            }
        }
    }

    // Call when no route was found.
    fn handle_error<'r>(&self,
                        code: StatusCode,
                        req: &'r Request,
                        response: FreshHyperResponse) {
        // Find the catcher or use the one for internal server errors.
        let catcher = self.catchers.get(&code.to_u16()).unwrap_or_else(|| {
            error_!("No catcher found for {}.", code);
            warn_!("Using internal server error catcher.");
            self.catchers.get(&500).expect("500 Catcher")
        });

        if let Some(mut responder) = catcher.handle(Error::NoRoute, req).responder() {
            if !responder.respond(response).is_success() {
                error_!("Catcher outcome was unsuccessul; aborting response.");
            } else {
                info_!("Responded with {} catcher.", White.paint(code));
            }
        } else {
            error_!("Catcher returned an incomplete response.");
            warn_!("Using default error response.");
            let catcher = self.default_catchers.get(&code.to_u16())
                .unwrap_or(self.default_catchers.get(&500).expect("500 default"));
            let responder = catcher.handle(Error::Internal, req).responder();
            responder.unwrap().respond(response).unwrap()
        }
{'fraction_non_alphanumeric': 0.1261562378546444, 'fraction_numerical': 0.0035755926933540615, 'mean_word_length': 2.0095906432748536, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 44, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5958093', 'n_tokens_mistral': 3568, 'n_tokens_neox': 3477, 'n_words': 966}