<commit_before>extern crate cryt;
extern crate clap;

use clap::{App, Arg, SubCommand};
use std::io::{self, Read, Write};

use cryt::criteria;
use cryt::encoding;
use cryt::xor::{self, Xor};

fn main() {
    let matches = App::new("cryt")
                          .version("v0.1")
                          .author("Nicolas Schmidt <nfschmidt@gmail.com>")
                          .about("Cryptographic tools")
                          .subcommand(SubCommand::with_name("encode")
                                      .about("Encode input with the specified encoding")
                                      .subcommand(SubCommand::with_name("hex")
                                                  .about("Encode input in hex"))
                                      .subcommand(SubCommand::with_name("base64")
                                                  .about("Encode input in base64")))
                          .subcommand(SubCommand::with_name("decode")
                                      .about("Decode input with the specified encoding")
                                      .subcommand(SubCommand::with_name("hex")
                                                  .about("Decode input in hex"))
                                      .subcommand(SubCommand::with_name("base64")
                                                  .about("Decode input in base64")))
                          .subcommand(SubCommand::with_name("encrypt")
                                      .about("Encrypt input with the specified algorithm")
                                      .subcommand(SubCommand::with_name("xor")
                                                  .about("Encrypt using xor")
                                                  .arg(Arg::with_name("key")
                                                       .short("k")
                                                       .long("key")
                                                       .takes_value(true)
                                                       .required(true)
                                                       .help("xor key to be used"))))
                          .subcommand(SubCommand::with_name("encrypt")
                                      .about("Decrypt input with the specified algorithm")
                                      .subcommand(SubCommand::with_name("xor")
                                                  .about("Decrypt using xor")
                                                  .arg(Arg::with_name("key")
                                                       .short("k")
                                                       .long("key")
                                                       .takes_value(true)
                                                       .required(true)
                                                       .help("xor key to be used"))))
                          .subcommand(SubCommand::with_name("attack")
                                      .about("Attack the specified encryption algorithm to decrypt the input")
                                      .subcommand(SubCommand::with_name("xor")
                                                  .about("Attack xor encrypted input")
                                                  .arg(Arg::with_name("criterion")
                                                       .short("c")
                                                       .long("criterion")
                                                       .takes_value(true)
                                                       .possible_values(&["printable", "text"])
                                                       .help("criterion to be used for scoring the results"))
                                                  .arg(Arg::with_name("detailed")
                                                       .short("d")
                                                       .long("detailed")
                                                       .required(false)
                                                       .help("Print decrypted result, the key and the score for the selected criterion"))
                                                  .subcommand(SubCommand::with_name("keysize")
                                                              .about("Determine the keysize of a repeated xor encryption")
                                                              .arg(Arg::with_name("criterion")
                                                                   .help("Criterion to determine keysize")
                                                                   .short("c")
                                                                   .long("criterion")
                                                                   .takes_value(true)
                                                                   .possible_values(&["hamming-distance"])
                                                                   .required(false))
                                                              .arg(Arg::with_name("min")
                                                                   .help("Minimum keysize to try")
                                                                   .long("min")
                                                                   .takes_value(true)
                                                                   .required(false))
                                                              .arg(Arg::with_name("max")
                                                                   .help("Maximum keysize to try")
                                                                   .long("max")
                                                                   .short("m")
                                                                   .takes_value(true)
                                                                   .required(true)))
                                                  .subcommand(SubCommand::with_name("repeated")
                                                              .about("Attack repeated xor encrypted input")
                                                              .arg(Arg::with_name("keysize-criterion")
                                                                   .help("Criterion to determine keysize")
                                                                   .short("k")
                                                                   .long("keysize-criterion")
                                                                   .takes_value(true)
                                                                   .possible_values(&["hamming-distance"])
                                                                   .required(false))
                                                              .arg(Arg::with_name("criterion")
                                                                   .short("c")
                                                                   .long("criterion")
                                                                   .takes_value(true)
                                                                   .possible_values(&["printable", "text"])
                                                                   .help("criterion to be used for scoring the intermediate block results"))
                                                              .arg(Arg::with_name("result-criterion")
                                                                   .short("r")
                                                                   .long("result-criterion")
                                                                   .takes_value(true)
                                                                   .possible_values(&["printable", "text"])
                                                                   .help("criterion to be used for scoring the results for different keysizes"))
                                                              .arg(Arg::with_name("keysizes-try")
                                                                   .short("t")
                                                                   .long("keysizes-try")
                                                                   .takes_value(true)
                                                                   .required(false)
                                                                   .help("criterion to be used for scoring the results for different keysizes"))
                                                              .arg(Arg::with_name("min")
                                                                   .help("Minimum keysize to try")
                                                                   .long("min")
                                                                   .takes_value(true)
                                                                   .required(false))
                                                              .arg(Arg::with_name("max")
                                                                   .help("Maximum keysize to try")
                                                                   .long("max")
                                                                   .short("m")
                                                                   .takes_value(true)
                                                                   .required(true)))))
                          .get_matches();

    if let Some(matches) = matches.subcommand_matches("encode") {
        if let Some(_) = matches.subcommand_matches("base64") {
            run_encode_base64();
        } else if let Some(_) = matches.subcommand_matches("hex") {
            run_encode_hex();
        }
    } else if let Some(matches) = matches.subcommand_matches("decode") {
        if let Some(_) = matches.subcommand_matches("base64") {
            run_decode_base64();
        } else if let Some(_) = matches.subcommand_matches("hex") {
            run_decode_hex();
        }
    } else if let Some(matches) = matches.subcommand_matches("encrypt") {
        if let Some(matches) = matches.subcommand_matches("xor") {
            match matches.value_of("key") {
                Some(k) => {
                    run_encrypt_xor(k);
                }
                None => {
                    println!("Error: No key received!");
                }
            }
        }
    } else if let Some(matches) = matches.subcommand_matches("attack") {
        if let Some(xor_matches) = matches.subcommand_matches("xor") {
            if let Some(keysize_matches) = xor_matches.subcommand_matches("keysize") {
                let criterion = match keysize_matches.value_of("criterion") {
                    Some("hamming-distance") => xor::hamming_distance_criterion,
                    Some(_) => xor::hamming_distance_criterion,
                    None => xor::hamming_distance_criterion
                };

                let min = match keysize_matches.value_of("min") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                let max = match keysize_matches.value_of("max") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                run_attack_xor_keysize(criterion, min, max);
                return;
            } else if let Some(repeated_matches) = xor_matches.subcommand_matches("repeated") {
                let xor_criterion = match repeated_matches.value_of("criterion") {
                    Some("printable") => criteria::printable_bytes,
                    Some("text") => criteria::text_bytes,
                    Some(_) => criteria::printable_bytes,
                    None => criteria::printable_bytes
                };

                let result_criterion = match xor_matches.value_of("result-criterion") {
                    Some("printable") => criteria::printable_bytes,
                    Some("text") => criteria::text_bytes,
                    Some(_) => criteria::text_bytes,
                    None => criteria::text_bytes
                };

                let keysize_criterion = match repeated_matches.value_of("keysize-criterion") {
                    Some("hamming-distance") => xor::hamming_distance_criterion,
                    Some(_) => xor::hamming_distance_criterion,
                    None => xor::hamming_distance_criterion
                };

                let keysize_try = match repeated_matches.value_of("keysizes-try") {
                    Some(v) => v.parse::<usize>().unwrap(),
                    None => 1
                };

                let min = match repeated_matches.value_of("min") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                let max = match repeated_matches.value_of("max") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                run_attack_xor_repeated(keysize_criterion, min, max, keysize_try, xor_criterion, result_criterion);
                return;
            }

            let criterion = match xor_matches.value_of("criterion") {
                Some("printable") => criteria::printable_bytes,
                Some("text") => criteria::text_bytes,
                Some(_) => criteria::printable_bytes,
                None => criteria::printable_bytes
            };

            if xor_matches.is_present("detailed") {
                run_attack_xor_detailed(criterion);
            } else {
                run_attack_xor(criterion);
            }
        }
    } else {
        run_interpreter();
    }
}

fn run_encode_base64() {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = encoding::base64_encode(&input);
    print!("{}", result);
}

fn run_encode_hex() {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = encoding::hex_encode(&input);
    print!("{}", result);
}

fn run_decode_base64() {
    let mut input = String::new();
    match io::stdin().read_to_string(&mut input) {
        Ok(_) => {
            let result = encoding::base64_decode(&input);
            io::stdout().write(&result).unwrap();
        }
        Err(error) => { println!("Error: {}", error); }
    }
}

fn run_decode_hex() {
    let mut input = String::new();
    match io::stdin().read_to_string(&mut input) {
        Ok(_) => {
            let result = encoding::hex_decode(&input);
            io::stdout().write(&result).unwrap();
        }
        Err(error) => { println!("Error: {}", error); }
    }
}

fn run_encrypt_xor(key: &str) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = Xor::new(key.as_bytes()).encrypt(&input);
    io::stdout().write(&result).unwrap();
}

fn run_attack_xor(criterion: fn(&[u8]) -> f32) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let (_, _, decrypted) = xor::SingleByteAttack::new()
        .with_criterion(Box::new(criterion))
        .result(&input);

    io::stdout().write(&decrypted).unwrap();
}

fn run_attack_xor_detailed(criterion: fn(&[u8]) -> f32) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let (key, score, decrypted) = xor::SingleByteAttack::new()
        .with_criterion(Box::new(criterion))
        .result(&input);

    print!("Key: {}\tScore: {}\tResult: ", key, score);
    io::stdout().write(&decrypted).unwrap();
    println!("");
}

fn run_attack_xor_keysize(criterion: fn(&[u8], size: u32) -> f32, min: u32, max: u32) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let results = xor::KeysizeAttack::new()
        .with_min_length(min)
        .with_max_length(max)
        .with_criterion(Box::new(criterion))
        .result(&input);

    for (size, score) in results {
        println!("Size: {}\tScore: {}", size, score);
    }
}

fn run_attack_xor_repeated(keysize_criterion: fn(&[u8], size: u32) -> f32, min: u32, max: u32, keysize_try: usize, xor_criterion: fn(&[u8]) -> f32, result_criterion: fn(&[u8]) -> f32) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let (key, decrypted) = xor::RepeatedAttack::new()
        .with_single_byte_attack(xor::SingleByteAttack::new()
                                 .with_criterion(Box::new(xor_criterion)))
        .with_keysize_attack(xor::KeysizeAttack::new()
                             .with_min_length(min)
                             .with_max_length(max)
                             .with_criterion(Box::new(keysize_criterion)))
        .with_result_criterion(Box::new(result_criterion))
        .with_keysizes_count(keysize_try)
        .result(&input);

    let mut stdout = io::stdout();
    print!("Key: ");
    stdout.write(&key).unwrap();
    print!("\nDecrypted:\n");
    stdout.write(&decrypted).unwrap();
}

fn run_interpreter() {
    println!("Interpreter");
}
<commit_msg>added decrypt and change args names<commit_after>extern crate cryt;
extern crate clap;

use clap::{App, Arg, SubCommand};
use std::io::{self, Read, Write};

use cryt::criteria;
use cryt::encoding;
use cryt::xor::{self, Xor};

fn main() {
    let matches = App::new("cryt")
                          .version("v0.1")
                          .author("Nicolas Schmidt <nfschmidt@gmail.com>")
                          .about("Cryptographic tools")
                          .subcommand(SubCommand::with_name("encode")
                                      .about("Encode input with the specified encoding")
                                      .subcommand(SubCommand::with_name("hex")
                                                  .about("Encode input in hex"))
                                      .subcommand(SubCommand::with_name("base64")
                                                  .about("Encode input in base64")))
                          .subcommand(SubCommand::with_name("decode")
                                      .about("Decode input with the specified encoding")
                                      .subcommand(SubCommand::with_name("hex")
                                                  .about("Decode input in hex"))
                                      .subcommand(SubCommand::with_name("base64")
                                                  .about("Decode input in base64")))
                          .subcommand(SubCommand::with_name("encrypt")
                                      .about("Encrypt input with the specified algorithm")
                                      .subcommand(SubCommand::with_name("xor")
                                                  .about("Encrypt using xor")
                                                  .arg(Arg::with_name("key")
                                                       .short("k")
                                                       .long("key")
                                                       .takes_value(true)
                                                       .required(true)
                                                       .help("xor key to be used"))))
                          .subcommand(SubCommand::with_name("decrypt")
                                      .about("Decrypt input with the specified algorithm")
                                      .subcommand(SubCommand::with_name("xor")
                                                  .about("Decrypt using xor")
                                                  .arg(Arg::with_name("key")
                                                       .short("k")
                                                       .long("key")
                                                       .takes_value(true)
                                                       .required(true)
                                                       .help("xor key to be used"))))
                          .subcommand(SubCommand::with_name("attack")
                                      .about("Attack the specified encryption algorithm to decrypt the input")
                                      .subcommand(SubCommand::with_name("xor")
                                                  .about("Attack xor encrypted input")
                                                  .arg(Arg::with_name("criterion")
                                                       .short("c")
                                                       .long("criterion")
                                                       .takes_value(true)
                                                       .possible_values(&["printable", "text"])
                                                       .help("criterion to be used for scoring the results"))
                                                  .arg(Arg::with_name("detailed")
                                                       .short("d")
                                                       .long("detailed")
                                                       .required(false)
                                                       .help("Print decrypted result, the key and the score for the selected criterion"))
                                                  .subcommand(SubCommand::with_name("keysize")
                                                              .about("Determine the keysize of a repeated xor encryption")
                                                              .arg(Arg::with_name("criterion")
                                                                   .help("Criterion to determine keysize")
                                                                   .short("c")
                                                                   .long("criterion")
                                                                   .takes_value(true)
                                                                   .possible_values(&["hamming-distance"])
                                                                   .required(false))
                                                              .arg(Arg::with_name("min")
                                                                   .help("Minimum keysize to try")
                                                                   .long("min")
                                                                   .takes_value(true)
                                                                   .required(false))
                                                              .arg(Arg::with_name("max")
                                                                   .help("Maximum keysize to try")
                                                                   .long("max")
                                                                   .short("m")
                                                                   .takes_value(true)
                                                                   .required(true)))
                                                  .subcommand(SubCommand::with_name("repeated")
                                                              .about("Attack repeated xor encrypted input")
                                                              .arg(Arg::with_name("keysize-criterion")
                                                                   .help("Criterion to determine keysize")
                                                                   .short("k")
                                                                   .long("keysize-criterion")
                                                                   .takes_value(true)
                                                                   .possible_values(&["hamming-distance"])
                                                                   .required(false))
                                                              .arg(Arg::with_name("xor-criterion")
                                                                   .short("x")
                                                                   .long("xor-criterion")
                                                                   .takes_value(true)
                                                                   .possible_values(&["printable", "text"])
                                                                   .help("criterion to be used for scoring the intermediate block results"))
                                                              .arg(Arg::with_name("criterion")
                                                                   .short("c")
                                                                   .long("criterion")
                                                                   .takes_value(true)
                                                                   .possible_values(&["printable", "text"])
                                                                   .help("criterion to be used for scoring the results for different keysizes"))
                                                              .arg(Arg::with_name("keysizes-try")
                                                                   .short("t")
                                                                   .long("keysizes-try")
                                                                   .takes_value(true)
                                                                   .required(false)
                                                                   .help("criterion to be used for scoring the results for different keysizes"))
                                                              .arg(Arg::with_name("min")
                                                                   .help("Minimum keysize to try")
                                                                   .long("min")
                                                                   .takes_value(true)
                                                                   .required(false))
                                                              .arg(Arg::with_name("max")
                                                                   .help("Maximum keysize to try")
                                                                   .long("max")
                                                                   .short("m")
                                                                   .takes_value(true)
                                                                   .required(true)))))
                          .get_matches();

    if let Some(matches) = matches.subcommand_matches("encode") {
        if let Some(_) = matches.subcommand_matches("base64") {
            run_encode_base64();
        } else if let Some(_) = matches.subcommand_matches("hex") {
            run_encode_hex();
        }
    } else if let Some(matches) = matches.subcommand_matches("decode") {
        if let Some(_) = matches.subcommand_matches("base64") {
            run_decode_base64();
        } else if let Some(_) = matches.subcommand_matches("hex") {
            run_decode_hex();
        }
    } else if let Some(matches) = matches.subcommand_matches("encrypt") {
        if let Some(matches) = matches.subcommand_matches("xor") {
            match matches.value_of("key") {
                Some(k) => {
                    run_encrypt_xor(k);
                }
                None => {
                    println!("Error: No key received!");
                }
            }
        }
    } else if let Some(matches) = matches.subcommand_matches("decrypt") {
        if let Some(matches) = matches.subcommand_matches("xor") {
            match matches.value_of("key") {
                Some(k) => {
                    run_decrypt_xor(k);
                }
                None => {
                    println!("Error: No key received!");
                }
            }
        }
    } else if let Some(matches) = matches.subcommand_matches("attack") {
        if let Some(xor_matches) = matches.subcommand_matches("xor") {
            if let Some(keysize_matches) = xor_matches.subcommand_matches("keysize") {
                let criterion = match keysize_matches.value_of("criterion") {
                    Some("hamming-distance") => xor::hamming_distance_criterion,
                    Some(_) => xor::hamming_distance_criterion,
                    None => xor::hamming_distance_criterion
                };

                let min = match keysize_matches.value_of("min") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                let max = match keysize_matches.value_of("max") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                run_attack_xor_keysize(criterion, min, max);
                return;
            } else if let Some(repeated_matches) = xor_matches.subcommand_matches("repeated") {
                let xor_criterion = match repeated_matches.value_of("xor-criterion") {
                    Some("printable") => criteria::printable_bytes,
                    Some("text") => criteria::text_bytes,
                    Some(_) => criteria::printable_bytes,
                    None => criteria::printable_bytes
                };

                let result_criterion = match xor_matches.value_of("criterion") {
                    Some("printable") => criteria::printable_bytes,
                    Some("text") => criteria::text_bytes,
                    Some(_) => criteria::text_bytes,
                    None => criteria::text_bytes
                };

                let keysize_criterion = match repeated_matches.value_of("keysize-criterion") {
                    Some("hamming-distance") => xor::hamming_distance_criterion,
                    Some(_) => xor::hamming_distance_criterion,
                    None => xor::hamming_distance_criterion
                };

                let keysize_try = match repeated_matches.value_of("keysizes-try") {
                    Some(v) => v.parse::<usize>().unwrap(),
                    None => 1
                };

                let min = match repeated_matches.value_of("min") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                let max = match repeated_matches.value_of("max") {
                    Some(v) => v.parse::<u32>().unwrap(),
                    None => 1
                };

                run_attack_xor_repeated(keysize_criterion, min, max, keysize_try, xor_criterion, result_criterion);
                return;
            }

            let criterion = match xor_matches.value_of("criterion") {
                Some("printable") => criteria::printable_bytes,
                Some("text") => criteria::text_bytes,
                Some(_) => criteria::printable_bytes,
                None => criteria::printable_bytes
            };

            if xor_matches.is_present("detailed") {
                run_attack_xor_detailed(criterion);
            } else {
                run_attack_xor(criterion);
            }
        }
    } else {
        run_interpreter();
    }
}

fn run_encode_base64() {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = encoding::base64_encode(&input);
    print!("{}", result);
}

fn run_encode_hex() {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = encoding::hex_encode(&input);
    print!("{}", result);
}

fn run_decode_base64() {
    let mut input = String::new();
    match io::stdin().read_to_string(&mut input) {
        Ok(_) => {
            let result = encoding::base64_decode(&input);
            io::stdout().write(&result).unwrap();
        }
        Err(error) => { println!("Error: {}", error); }
    }
}

fn run_decode_hex() {
    let mut input = String::new();
    match io::stdin().read_to_string(&mut input) {
        Ok(_) => {
            let result = encoding::hex_decode(&input);
            io::stdout().write(&result).unwrap();
        }
        Err(error) => { println!("Error: {}", error); }
    }
}

fn run_encrypt_xor(key: &str) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = Xor::new(key.as_bytes()).encrypt(&input);
    io::stdout().write(&result).unwrap();
}

fn run_decrypt_xor(key: &str) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let result = Xor::new(key.as_bytes()).decrypt(&input);
    io::stdout().write(&result).unwrap();
}

fn run_attack_xor(criterion: fn(&[u8]) -> f32) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let (_, _, decrypted) = xor::SingleByteAttack::new()
        .with_criterion(Box::new(criterion))
        .result(&input);

    io::stdout().write(&decrypted).unwrap();
}

fn run_attack_xor_detailed(criterion: fn(&[u8]) -> f32) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let (key, score, decrypted) = xor::SingleByteAttack::new()
        .with_criterion(Box::new(criterion))
        .result(&input);

    print!("Key: {}\tScore: {}\tResult: ", key, score);
    io::stdout().write(&decrypted).unwrap();
    println!("");
}

fn run_attack_xor_keysize(criterion: fn(&[u8], size: u32) -> f32, min: u32, max: u32) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let results = xor::KeysizeAttack::new()
        .with_min_length(min)
        .with_max_length(max)
        .with_criterion(Box::new(criterion))
        .result(&input);

    for (size, score) in results {
        println!("Size: {}\tScore: {}", size, score);
    }
}

fn run_attack_xor_repeated(keysize_criterion: fn(&[u8], size: u32) -> f32, min: u32, max: u32, keysize_try: usize, xor_criterion: fn(&[u8]) -> f32, result_criterion: fn(&[u8]) -> f32) {
    let mut input = Vec::new();
    io::stdin().read_to_end(&mut input).unwrap();

    let (key, decrypted) = xor::RepeatedAttack::new()
        .with_single_byte_attack(xor::SingleByteAttack::new()
                                 .with_criterion(Box::new(xor_criterion)))
        .with_keysize_attack(xor::KeysizeAttack::new()
                             .with_min_length(min)
                             .with_max_length(max)
                             .with_criterion(Box::new(keysize_criterion)))
        .with_result_criterion(Box::new(result_criterion))
        .with_keysizes_count(keysize_try)
        .result(&input);

    let mut stdout = io::stdout();
    print!("Key: ");
    stdout.write(&key).unwrap();
    print!("\nDecrypted:\n");
    stdout.write(&decrypted).unwrap();
}

fn run_interpreter() {
    println!("Interpreter");
}
<|endoftext|>


<commit_before>#[phase(plugin)]
extern crate peg_syntax_ext;

#[deriving(Show, PartialEq)]
pub enum AST {
    Atom(String),
    Integer(i64),
}

#[deriving(PartialEq)]
pub enum IntegerSign {
    Positive,
    Negative,
}

pub mod integer {
    use parser::AST;
    use parser::IntegerSign;

    pub fn parse_decimal(str: &str, sign: &IntegerSign) -> AST {
        let integer: i64 = from_str(str).unwrap();

        AST::Integer(
            if *sign == IntegerSign::Negative {
                -integer
            } else {
                integer
            }
        )
    }

    pub fn parse_sign(str: &str) -> IntegerSign {
        match str {
            "-" => IntegerSign::Negative,
            _   => IntegerSign::Positive,
        }
    }
}

peg! ast(r#"

use parser::*;

#[pub]
value -> AST =
    integer
    / identifier

identifier -> AST =
    initial subsequent* {
        AST::Atom(match_str.to_string())
    }
    / peculiar_identifier {
        AST::Atom(match_str.to_string())
    }

initial -> &'input str =
    constituent
    / special_initial

constituent -> &'input str =
    letter

letter -> &'input str =
    [a-zA-Z] { match_str }

special_initial -> &'input str =
    [!$%&*/<=>?^_~] { match_str }

subsequent -> &'input str =
    initial
    / digit
    / special_subsequent

digit -> &'input str =
    [0-9] { match_str }

peculiar_identifier -> &'input str =
    (
        "->" subsequent*
        / "..."
        / "+"
        / "-"
    )
    { match_str }

special_subsequent -> &'input str =
    [+-.@] { match_str }

integer -> AST =
    sign:sign digits:digits {
        integer::parse_decimal(digits, &sign)
    }

digits -> &'input str =
    [0-9]+ { match_str }

sign -> IntegerSign =
    [-+]? {
        integer::parse_sign(match_str)
    }

"#)

pub fn parse(input: &str) -> Result<AST, String> {
    ast::value(input)
}
<commit_msg>Moved atom AST generation to an external function.<commit_after>#[phase(plugin)]
extern crate peg_syntax_ext;

#[deriving(Show, PartialEq)]
pub enum AST {
    Atom(String),
    Integer(i64),
}

#[deriving(PartialEq)]
pub enum IntegerSign {
    Positive,
    Negative,
}

pub mod integer {
    use parser::AST;
    use parser::IntegerSign;

    pub fn parse_decimal(str: &str, sign: &IntegerSign) -> AST {
        let integer: i64 = from_str(str).unwrap();

        AST::Integer(
            if *sign == IntegerSign::Negative {
                -integer
            } else {
                integer
            }
        )
    }

    pub fn parse_sign(str: &str) -> IntegerSign {
        match str {
            "-" => IntegerSign::Negative,
            _   => IntegerSign::Positive,
        }
    }
}

pub mod atom {
    use parser::AST;

    pub fn parse(str: &str) -> AST {
        AST::Atom(str.to_string())
    }
}

peg! ast(r#"

use parser::*;

#[pub]
value -> AST =
    integer
    / identifier

identifier -> AST =
    initial subsequent* {
        atom::parse(match_str)
    }
    / peculiar_identifier {
        atom::parse(match_str)
    }

initial -> &'input str =
    constituent
    / special_initial

constituent -> &'input str =
    letter

letter -> &'input str =
    [a-zA-Z] { match_str }

special_initial -> &'input str =
    [!$%&*/<=>?^_~] { match_str }

subsequent -> &'input str =
    initial
    / digit
    / special_subsequent

digit -> &'input str =
    [0-9] { match_str }

peculiar_identifier -> &'input str =
    (
        "->" subsequent*
        / "..."
        / "+"
        / "-"
    )
    { match_str }

special_subsequent -> &'input str =
    [+-.@] { match_str }

integer -> AST =
    sign:sign digits:digits {
        integer::parse_decimal(digits, &sign)
    }

digits -> &'input str =
    [0-9]+ { match_str }

sign -> IntegerSign =
    [-+]? {
        integer::parse_sign(match_str)
    }

"#)

pub fn parse(input: &str) -> Result<AST, String> {
    ast::value(input)
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 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.

//! Implements a NATS Streaming-based event stream.

use super::EventStream;
use crate::error::Result;
use futures::{sync::mpsc::unbounded,
              Future,
              Stream};
use nitox::{commands::ConnectCommand,
            streaming::{client::NatsStreamingClient,
                        error::NatsStreamingError},
            NatsClient,
            NatsClientOptions};
use std::{sync::mpsc::sync_channel,
          thread};
use tokio::{executor,
            runtime::current_thread::Runtime as ThreadRuntime};

/// All messages are published under this subject.
const HABITAT_SUBJECT: &str = "habitat";

/// All the information needed to establish a connection to a NATS
/// Streaming server.
// TODO: This will change as we firm up what the interaction between
// Habitat and A2 looks like.
pub struct EventConnectionInfo {
    pub name:        String,
    pub verbose:     bool,
    pub cluster_uri: String,
    pub cluster_id:  String,
}

/// Defines default connection information for a NATS Streaming server
/// running on localhost.
// TODO: As we become clear on the interaction between Habitat and A2,
// this implementation *may* disappear. It's useful for testing and
// prototyping, though.
impl Default for EventConnectionInfo {
    fn default() -> Self {
        EventConnectionInfo { name:        String::from("habitat"),
                              verbose:     true,
                              cluster_uri: String::from("127.0.0.1:4223"),
                              cluster_id:  String::from("test-cluster"), }
    }
}

pub(super) fn init_stream(conn_info: EventConnectionInfo) -> Result<EventStream> {
    // TODO (CM): Investigate back-pressure scenarios
    let (event_tx, event_rx) = unbounded();
    let (sync_tx, sync_rx) = sync_channel(0); // rendezvous channel

    // TODO (CM): We could theoretically create this future and spawn
    // it in the Supervisor's Tokio runtime, but there's currently a
    // bug: https://github.com/YellowInnovation/nitox/issues/24

    thread::Builder::new().name("events".to_string())
                          .spawn(move || {
                              let EventConnectionInfo { name,
                                                        verbose,
                                                        cluster_uri,
                                                        cluster_id, } = conn_info;

                              let cc = ConnectCommand::builder()
                // .user(Some("nats".to_string()))
                // .pass(Some("S3Cr3TP@5w0rD".to_string()))
                .name(Some(name))
                .verbose(verbose)
                .build()
                .unwrap();
                              let opts =
                                  NatsClientOptions::builder().connect_command(cc)
                                                              .cluster_uri(cluster_uri.as_str())
                                                              .build()
                                                              .unwrap();

                              let publisher = NatsClient::from_options(opts)
                .map_err(Into::<NatsStreamingError>::into)
                .and_then(|client| {
                    NatsStreamingClient::from(client)
                        .cluster_id(cluster_id)
                        .connect()
                })
                .map_err(|streaming_error| error!("{}", streaming_error))
                .and_then(move |client| {
                    sync_tx.send(()).expect("Couldn't synchronize!");
                    event_rx.for_each(move |event: Vec<u8>| {
                        let publish_event = client
                            .publish(HABITAT_SUBJECT.into(), event.into())
                            .map_err(|e| {
                                error!("Error publishing event: {:?}", e);
                            });
                        executor::spawn(publish_event);
                        Ok(())
                    })
                });

                              ThreadRuntime::new().expect("Couldn't create event stream runtime!")
                                                  .spawn(publisher)
                                                  .run()
                                                  .expect("something seriously wrong has occurred");
                          })
                          .expect("Couldn't start events thread!");

    sync_rx.recv()?; // TODO (CM): nicer error message
    Ok(EventStream(event_tx))
}
<commit_msg>[PR Feedback] Use more explicit channel function calls for clarity<commit_after>// Copyright (c) 2019 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.

//! Implements a NATS Streaming-based event stream.

use super::EventStream;
use crate::error::Result;
use futures::{sync::mpsc as futures_mpsc,
              Future,
              Stream};
use nitox::{commands::ConnectCommand,
            streaming::{client::NatsStreamingClient,
                        error::NatsStreamingError},
            NatsClient,
            NatsClientOptions};
use std::{sync::mpsc as std_mpsc,
          thread};
use tokio::{executor,
            runtime::current_thread::Runtime as ThreadRuntime};

/// All messages are published under this subject.
const HABITAT_SUBJECT: &str = "habitat";

/// All the information needed to establish a connection to a NATS
/// Streaming server.
// TODO: This will change as we firm up what the interaction between
// Habitat and A2 looks like.
pub struct EventConnectionInfo {
    pub name:        String,
    pub verbose:     bool,
    pub cluster_uri: String,
    pub cluster_id:  String,
}

/// Defines default connection information for a NATS Streaming server
/// running on localhost.
// TODO: As we become clear on the interaction between Habitat and A2,
// this implementation *may* disappear. It's useful for testing and
// prototyping, though.
impl Default for EventConnectionInfo {
    fn default() -> Self {
        EventConnectionInfo { name:        String::from("habitat"),
                              verbose:     true,
                              cluster_uri: String::from("127.0.0.1:4223"),
                              cluster_id:  String::from("test-cluster"), }
    }
}

pub(super) fn init_stream(conn_info: EventConnectionInfo) -> Result<EventStream> {
    // TODO (CM): Investigate back-pressure scenarios
    let (event_tx, event_rx) = futures_mpsc::unbounded();
    let (sync_tx, sync_rx) = std_mpsc::sync_channel(0); // rendezvous channel

    // TODO (CM): We could theoretically create this future and spawn
    // it in the Supervisor's Tokio runtime, but there's currently a
    // bug: https://github.com/YellowInnovation/nitox/issues/24

    thread::Builder::new().name("events".to_string())
                          .spawn(move || {
                              let EventConnectionInfo { name,
                                                        verbose,
                                                        cluster_uri,
                                                        cluster_id, } = conn_info;

                              let cc = ConnectCommand::builder()
                // .user(Some("nats".to_string()))
                // .pass(Some("S3Cr3TP@5w0rD".to_string()))
                .name(Some(name))
                .verbose(verbose)
                .build()
                .unwrap();
                              let opts =
                                  NatsClientOptions::builder().connect_command(cc)
                                                              .cluster_uri(cluster_uri.as_str())
                                                              .build()
                                                              .unwrap();

                              let publisher = NatsClient::from_options(opts)
                .map_err(Into::<NatsStreamingError>::into)
                .and_then(|client| {
                    NatsStreamingClient::from(client)
                        .cluster_id(cluster_id)
                        .connect()
                })
                .map_err(|streaming_error| error!("{}", streaming_error))
                .and_then(move |client| {
                    sync_tx.send(()).expect("Couldn't synchronize!");
                    event_rx.for_each(move |event: Vec<u8>| {
                        let publish_event = client
                            .publish(HABITAT_SUBJECT.into(), event.into())
                            .map_err(|e| {
                                error!("Error publishing event: {:?}", e);
                            });
                        executor::spawn(publish_event);
                        Ok(())
                    })
                });

                              ThreadRuntime::new().expect("Couldn't create event stream runtime!")
                                                  .spawn(publisher)
                                                  .run()
                                                  .expect("something seriously wrong has occurred");
                          })
                          .expect("Couldn't start events thread!");

    sync_rx.recv()?; // TODO (CM): nicer error message
    Ok(EventStream(event_tx))
}
<|endoftext|>


<commit_before>extern "C" {
    fn evenmorehello();
}

pub fn helloer() {
    println!("I'm saying \"hello\" again!");
    unsafe { evenmorehello() };
}
<commit_msg>Update formatter to rustfmt 2.0<commit_after>extern "C" {
    fn evenmorehello();
}

pub fn helloer() {
    println!("I'm saying \"hello\" again!");
    unsafe {
        evenmorehello()
    };
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use parallel_letter_frequency as frequency;

// Poem by Friedrich Schiller. The corresponding music is the European Anthem.
const ODE_AN_DIE_FREUDE: [&str; 8] = [
    "Freude schöner Götterfunken",
    "Tochter aus Elysium,",
    "Wir betreten feuertrunken,",
    "Himmlische, dein Heiligtum!",
    "Deine Zauber binden wieder",
    "Was die Mode streng geteilt;",
    "Alle Menschen werden Brüder,",
    "Wo dein sanfter Flügel weilt.",
];

// Dutch national anthem
const WILHELMUS: [&str; 8] = [
    "Wilhelmus van Nassouwe",
    "ben ik, van Duitsen bloed,",
    "den vaderland getrouwe",
    "blijf ik tot in den dood.",
    "Een Prinse van Oranje",
    "ben ik, vrij, onverveerd,",
    "den Koning van Hispanje",
    "heb ik altijd geëerd.",
];

// American national anthem
const STAR_SPANGLED_BANNER: [&str; 8] = [
    "O say can you see by the dawn's early light,",
    "What so proudly we hailed at the twilight's last gleaming,",
    "Whose broad stripes and bright stars through the perilous fight,",
    "O'er the ramparts we watched, were so gallantly streaming?",
    "And the rockets' red glare, the bombs bursting in air,",
    "Gave proof through the night that our flag was still there;",
    "O say does that star-spangled banner yet wave,",
    "O'er the land of the free and the home of the brave?",
];

#[test]
fn test_no_texts() {
    assert_eq!(frequency::frequency(&[], 4), HashMap::new());
}

#[test]
#[ignore]
fn test_one_letter() {
    let mut hm = HashMap::new();
    hm.insert('a', 1);
    assert_eq!(frequency::frequency(&["a"], 4), hm);
}

#[test]
#[ignore]
fn test_case_insensitivity() {
    let mut hm = HashMap::new();
    hm.insert('a', 2);
    assert_eq!(frequency::frequency(&["aA"], 4), hm);
}

#[test]
#[ignore]
fn test_many_empty_lines() {
    let mut v = Vec::with_capacity(1000);
    for _ in 0..1000 {
        v.push("");
    }
    assert_eq!(frequency::frequency(&v[..], 4), HashMap::new());
}

#[test]
#[ignore]
fn test_many_times_same_text() {
    let mut v = Vec::with_capacity(1000);
    for _ in 0..1000 {
        v.push("abc");
    }
    let mut hm = HashMap::new();
    hm.insert('a', 1000);
    hm.insert('b', 1000);
    hm.insert('c', 1000);
    assert_eq!(frequency::frequency(&v[..], 4), hm);
}

#[test]
#[ignore]
fn test_punctuation_doesnt_count() {
    assert!(!frequency::frequency(&WILHELMUS, 4).contains_key(&','));
}

#[test]
#[ignore]
fn test_numbers_dont_count() {
    assert!(!frequency::frequency(&["Testing, 1, 2, 3"], 4).contains_key(&'1'));
}

#[test]
#[ignore]
fn test_all_three_anthems_1_worker() {
    let mut v = Vec::new();
    for anthem in [ODE_AN_DIE_FREUDE, WILHELMUS, STAR_SPANGLED_BANNER].iter() {
        for line in anthem.iter() {
            v.push(*line);
        }
    }
    let freqs = frequency::frequency(&v[..], 1);
    assert_eq!(freqs.get(&'a'), Some(&49));
    assert_eq!(freqs.get(&'t'), Some(&56));
    assert_eq!(freqs.get(&'ü'), Some(&2));
}

#[test]
#[ignore]
fn test_all_three_anthems_3_workers() {
    let mut v = Vec::new();
    for anthem in [ODE_AN_DIE_FREUDE, WILHELMUS, STAR_SPANGLED_BANNER].iter() {
        for line in anthem.iter() {
            v.push(*line);
        }
    }
    let freqs = frequency::frequency(&v[..], 3);
    assert_eq!(freqs.get(&'a'), Some(&49));
    assert_eq!(freqs.get(&'t'), Some(&56));
    assert_eq!(freqs.get(&'ü'), Some(&2));
}
<commit_msg>[parallel-letter-frequency]: Clippy says to use resize or static creation (#998)<commit_after>use std::collections::HashMap;

use parallel_letter_frequency as frequency;

// Poem by Friedrich Schiller. The corresponding music is the European Anthem.
const ODE_AN_DIE_FREUDE: [&str; 8] = [
    "Freude schöner Götterfunken",
    "Tochter aus Elysium,",
    "Wir betreten feuertrunken,",
    "Himmlische, dein Heiligtum!",
    "Deine Zauber binden wieder",
    "Was die Mode streng geteilt;",
    "Alle Menschen werden Brüder,",
    "Wo dein sanfter Flügel weilt.",
];

// Dutch national anthem
const WILHELMUS: [&str; 8] = [
    "Wilhelmus van Nassouwe",
    "ben ik, van Duitsen bloed,",
    "den vaderland getrouwe",
    "blijf ik tot in den dood.",
    "Een Prinse van Oranje",
    "ben ik, vrij, onverveerd,",
    "den Koning van Hispanje",
    "heb ik altijd geëerd.",
];

// American national anthem
const STAR_SPANGLED_BANNER: [&str; 8] = [
    "O say can you see by the dawn's early light,",
    "What so proudly we hailed at the twilight's last gleaming,",
    "Whose broad stripes and bright stars through the perilous fight,",
    "O'er the ramparts we watched, were so gallantly streaming?",
    "And the rockets' red glare, the bombs bursting in air,",
    "Gave proof through the night that our flag was still there;",
    "O say does that star-spangled banner yet wave,",
    "O'er the land of the free and the home of the brave?",
];

#[test]
fn test_no_texts() {
    assert_eq!(frequency::frequency(&[], 4), HashMap::new());
}

#[test]
#[ignore]
fn test_one_letter() {
    let mut hm = HashMap::new();
    hm.insert('a', 1);
    assert_eq!(frequency::frequency(&["a"], 4), hm);
}

#[test]
#[ignore]
fn test_case_insensitivity() {
    let mut hm = HashMap::new();
    hm.insert('a', 2);
    assert_eq!(frequency::frequency(&["aA"], 4), hm);
}

#[test]
#[ignore]
fn test_many_empty_lines() {
    let v = vec![""; 1000];
    assert_eq!(frequency::frequency(&v[..], 4), HashMap::new());
}

#[test]
#[ignore]
fn test_many_times_same_text() {
    let v = vec!["abc"; 1000];
    let mut hm = HashMap::new();
    hm.insert('a', 1000);
    hm.insert('b', 1000);
    hm.insert('c', 1000);
    assert_eq!(frequency::frequency(&v[..], 4), hm);
}

#[test]
#[ignore]
fn test_punctuation_doesnt_count() {
    assert!(!frequency::frequency(&WILHELMUS, 4).contains_key(&','));
}

#[test]
#[ignore]
fn test_numbers_dont_count() {
    assert!(!frequency::frequency(&["Testing, 1, 2, 3"], 4).contains_key(&'1'));
}

#[test]
#[ignore]
fn test_all_three_anthems_1_worker() {
    let mut v = Vec::new();
    for anthem in [ODE_AN_DIE_FREUDE, WILHELMUS, STAR_SPANGLED_BANNER].iter() {
        for line in anthem.iter() {
            v.push(*line);
        }
    }
    let freqs = frequency::frequency(&v[..], 1);
    assert_eq!(freqs.get(&'a'), Some(&49));
    assert_eq!(freqs.get(&'t'), Some(&56));
    assert_eq!(freqs.get(&'ü'), Some(&2));
}

#[test]
#[ignore]
fn test_all_three_anthems_3_workers() {
    let mut v = Vec::new();
    for anthem in [ODE_AN_DIE_FREUDE, WILHELMUS, STAR_SPANGLED_BANNER].iter() {
        for line in anthem.iter() {
            v.push(*line);
        }
    }
    let freqs = frequency::frequency(&v[..], 3);
    assert_eq!(freqs.get(&'a'), Some(&49));
    assert_eq!(freqs.get(&'t'), Some(&56));
    assert_eq!(freqs.get(&'ü'), Some(&2));
}
<|endoftext|>


<commit_before>use rotor::Scope;
use rotor_stream::{Protocol, StreamSocket, Deadline, Expectation as E};
use rotor_stream::{Request, Transport};
use hyper::status::StatusCode;
use hyper::method::Method::Head;

use super::{MAX_HEADERS_NUM, MAX_HEADERS_SIZE};
use super::{Response};
use super::protocol::{Server, RecvMode};
use super::context::Context;
use super::request::Head;
use super::body::BodyKind;

pub enum Parser<M: Sized> {
    Idle,
    ReadHeaders,
    /// Buffered fixed-size request (bytes left)
    BufferFixed(M, usize),
    /// Buffered request till end of input
    BufferEOF(M),
    /// Buffered request with chunked encoding (bytes left for current chunk)
    BufferChunked(M, usize),
    /// Progressive fixed-size request (bytes left)
    ProgressiveFixed(M, u64),
    /// Progressive till end of input
    ProgressiveEOF(M),
    /// Progressive with chunked encoding (bytes left for current chunk)
    ProgressiveChunked(M, u64),
    DoneResponse,
}

impl<M> Parser<M>
{
    fn flush<C>(scope: &mut Scope<C>) -> Request<Parser<M>>
        where C: Context
    {
        Some((Parser::DoneResponse, E::Flush(0),
              Deadline::now() + scope.byte_timeout()))
    }
}

fn parse_headers<C, M, S>(transport: &mut Transport<S>, end: usize,
    scope: &mut Scope<C>) -> Request<Parser<M>>
    where M: Server<C, S>,
          S: StreamSocket,
          C: Context,
{
    // Determines if we can keep-alive after error response.
    // We may not be able to keep keep-alive for multiple reasons:
    //
    // 1. When request headers are too wrong
    //    (probably client connects with wrong protocol)
    //
    // 2. When request contains non-empty request body (we don't
    //    want to wait until it is uploaded just to send error)
    //
    // Note we definitely can't keep alive if we can't say
    // whether request method is HEAD
    //
    // All of these are important to avoid cache poisoning attacks
    // on proxy servers.
    let mut can_keep_alive = false;
    // Determines if we can safely send the response body
    let mut is_head = false;

    let status = match Head::parse(&transport.input()[..end+4]) {
        Ok(head) => {
            is_head = head.method == Head;
            match M::headers_received(&head, scope) {
                Ok((m, mode, dline)) => {
                    match BodyKind::parse(&head) {
                        Ok(body) => {
                            // TODO(tailhook)
                            // Probably can handle small
                            // request bodies that are already
                            // in the buffer
                            if body == BodyKind::Fixed(0) {
                                can_keep_alive = true;
                            }
                            Ok((head, body, m, mode, dline))
                        }
                        Err(status) => Err(status),
                    }
                }
                Err(status) => Err(status),
            }
        }
        Err(status) => Err(status),
    };
    transport.input().consume(end+4);
    match status {
        Ok((head, body, m, mode, dline)) => {
            // TODO(tailhook) first check for 101 expect, but not for http1.0
            unimplemented!();
        }
        Err(status) => {
            let mut resp = Response::new(transport, is_head);
            scope.emit_error_page(status, &mut resp);
            resp.done();
            return Parser::flush(scope);
        }
    }
}

impl<C, M, S> Protocol<C, S> for Parser<M>
    where M: Server<C, S>,
          S: StreamSocket,
          C: Context,
{
    type Seed = ();
    fn create(_seed: (), _sock: &mut S, scope: &mut Scope<C>)
        -> Request<Self>
    {
        Some((Parser::Idle, E::Bytes(1),
            Deadline::now() + scope.byte_timeout()))
    }
    fn bytes_read(self, transport: &mut Transport<S>,
                  end: usize, scope: &mut Scope<C>)
        -> Request<Self>
    {
        use self::Parser::*;
        match self {
            Idle => {
                Some((Parser::ReadHeaders,
                      E::Delimiter(b"\r\n\r\n", MAX_HEADERS_SIZE),
                      Deadline::now() + scope.byte_timeout()))
            }
            ReadHeaders => {
                parse_headers(transport, end, scope)
            }
            _ => unimplemented!(),
        }
    }
    fn bytes_flushed(self, _transport: &mut Transport<S>,
                     _scope: &mut Scope<C>)
        -> Request<Self>
    {
        unimplemented!();
    }
    fn timeout(self, _transport: &mut Transport<S>,
        _scope: &mut Scope<C>)
        -> Request<Self>
    {
        unimplemented!();
    }
    fn delimiter_not_found(self, _transport: &mut Transport<S>,
        scope: &mut Scope<C>)
        -> Request<Self>
    {
        use self::Parser::*;
        match self {
            Idle => None,
            ReadHeaders => {
                // TODO(tailhook) send RequestHeaderFieldsTooLarge
                Parser::flush(scope)
            }
            _ => unimplemented!(),
        }
    }
    fn wakeup(self, transport: &mut Transport<S>, _scope: &mut Scope<C>)
        -> Request<Self>
    {
        unimplemented!();
    }
}
<commit_msg>WIP: light refactoring of things<commit_after>use std::usize;
use std::cmp::min;

use netbuf;
use rotor::Scope;
use rotor_stream::{Protocol, StreamSocket, Deadline, Expectation as E};
use rotor_stream::{Request, Transport};
use hyper::status::StatusCode::{self, PayloadTooLarge};
use hyper::method::Method::Head;
use hyper::header::Expect;

use super::{MAX_HEADERS_NUM, MAX_HEADERS_SIZE};
use super::{Response};
use super::protocol::{Server, RecvMode};
use super::context::Context;
use super::request::Head;
use super::body::BodyKind;

struct ReadBody<M: Sized> {
    machine: M,
    deadline: Deadline,
    progress: BodyProgress,
}

pub enum BodyProgress {
    /// Buffered fixed-size request (bytes left)
    BufferFixed(usize),
    /// Buffered request till end of input
    BufferEOF,
    /// Buffered request with chunked encoding (bytes left for current chunk)
    BufferChunked(usize),
    /// Progressive fixed-size request (size hint, bytes left)
    ProgressiveFixed(usize, u64),
    /// Progressive till end of input (size hint)
    ProgressiveEOF(usize),
    /// Progressive with chunked encoding (hint, bytes left for current chunk)
    ProgressiveChunked(usize, u64),
}

pub enum Parser<M: Sized> {
    Idle,
    ReadHeaders,
    ReadingBody(ReadBody<M>),
    /// Close connection after buffer is flushed. In other cases -> Idle
    DoneResponse,
}

impl<M> Parser<M>
{
    fn flush<C>(scope: &mut Scope<C>) -> Request<Parser<M>>
        where C: Context
    {
        Some((Parser::DoneResponse, E::Flush(0),
              Deadline::now() + scope.byte_timeout()))
    }
}

fn start_headers<C: Context, M: Sized>(scope: &mut Scope<C>)
    -> Request<Parser<M>>
{
    Some((Parser::ReadHeaders,
          E::Delimiter(b"\r\n\r\n", MAX_HEADERS_SIZE),
          Deadline::now() + scope.byte_timeout()))
}

fn start_body(mode: RecvMode, body: BodyKind) -> BodyProgress {
    use super::body::BodyKind::*;
    use super::protocol::RecvMode::*;
    use self::BodyProgress::*;

    match (mode, body) {
        // The size of Fixed(x) is checked above
        (Buffered, Fixed(x)) => BufferFixed(x as usize),
        (Buffered, Chunked) => BufferChunked(0),
        (Buffered, Eof) => BufferEOF,
        (Progressive(x), Fixed(y)) => ProgressiveFixed(x, y),
        (Progressive(x), Chunked) => ProgressiveChunked(x, 0),
        (Progressive(x), Eof) => ProgressiveEOF(x),
        (_, Upgrade) => unimplemented!(),
    }
}

// Parses headers
//
// On error returns bool, which is true if keep-alive connection can be
// carried on.
fn parse_headers<C, M, S>(transport: &mut Transport<S>, end: usize,
    scope: &mut Scope<C>) -> Result<ReadBody<M>, bool>
    where M: Server<C, S>,
          S: StreamSocket,
          C: Context,
{
    use self::Parser::*;
    // Determines if we can keep-alive after error response.
    // We may not be able to keep keep-alive for multiple reasons:
    //
    // 1. When request headers are too wrong
    //    (probably client connects with wrong protocol)
    //
    // 2. When request contains non-empty request body (we don't
    //    want to wait until it is uploaded just to send error)
    //
    // Note we definitely can't keep alive if we can't say
    // whether request method is HEAD
    //
    // All of these are important to avoid cache poisoning attacks
    // on proxy servers.
    let mut can_keep_alive = false;
    // Determines if we can safely send the response body
    let mut is_head = false;

    // TODO(tailhook) this should be contant
    let MAX_BUFFERED_BODY: u64 = min(usize::MAX as u64,
                                     netbuf::MAX_BUF_SIZE as u64);

    let status = match Head::parse(&transport.input()[..end+4]) {
        Ok(head) => {
            is_head = head.method == Head;
            match M::headers_received(&head, scope) {
                Ok((m, mode, dline)) => {
                    match BodyKind::parse(&head) {
                        // Note this check is needed for avoiding overflow
                        // later
                        Ok(BodyKind::Fixed(x)) if x > MAX_BUFFERED_BODY
                        => Err(PayloadTooLarge),
                        Ok(body) => {
                            // TODO(tailhook)
                            // Probably can handle small
                            // request bodies that are already
                            // in the buffer
                            if body == BodyKind::Fixed(0) {
                                can_keep_alive = true;
                            }
                            Ok((head, body, m, mode, dline))
                        }
                        Err(status) => Err(status),
                    }
                }
                Err(status) => Err(status),
            }
        }
        Err(status) => Err(status),
    };
    transport.input().consume(end+4);
    match status {
        Ok((head, body, m, mode, dline)) => {
            if head.headers.get::<Expect>() == Some(&Expect::Continue) {
                // Handler has already approved request, so just push it
                transport.output().extend(
                    format!("{} 100 Continue\r\n\r\n", head.version)
                    .as_bytes());
            }
            Ok(ReadBody {
                machine: m,
                deadline: dline,
                progress: start_body(mode, body)
            })
        }
        Err(status) => {
            let mut resp = Response::new(transport, is_head);
            scope.emit_error_page(status, &mut resp);
            resp.done();
            Err(can_keep_alive)
        }
    }
}

impl<C, M, S> Protocol<C, S> for Parser<M>
    where M: Server<C, S>,
          S: StreamSocket,
          C: Context,
{
    type Seed = ();
    fn create(_seed: (), _sock: &mut S, scope: &mut Scope<C>)
        -> Request<Self>
    {
        Some((Parser::Idle, E::Bytes(1),
            Deadline::now() + scope.byte_timeout()))
    }
    fn bytes_read(self, transport: &mut Transport<S>,
                  end: usize, scope: &mut Scope<C>)
        -> Request<Self>
    {
        use self::Parser::*;
        match self {
            Idle => {
                start_headers(scope)
            }
            ReadHeaders => {
                match parse_headers::<C, M, S>(transport, end, scope) {
                    Ok(body) => {
                        unimplemented!();
                    }
                    Err(can_keep_alive) => {
                        if can_keep_alive {
                            start_headers(scope)
                        } else {
                            Parser::flush(scope)
                        }
                    }
                }
            }
            _ => unimplemented!(),
        }
    }
    fn bytes_flushed(self, _transport: &mut Transport<S>,
                     _scope: &mut Scope<C>)
        -> Request<Self>
    {
        unimplemented!();
    }
    fn timeout(self, _transport: &mut Transport<S>,
        _scope: &mut Scope<C>)
        -> Request<Self>
    {
        unimplemented!();
    }
    fn delimiter_not_found(self, _transport: &mut Transport<S>,
        scope: &mut Scope<C>)
        -> Request<Self>
    {
        use self::Parser::*;
        match self {
            Idle => None,
            ReadHeaders => {
                // TODO(tailhook) send RequestHeaderFieldsTooLarge
                Parser::flush(scope)
            }
            _ => unimplemented!(),
        }
    }
    fn wakeup(self, transport: &mut Transport<S>, _scope: &mut Scope<C>)
        -> Request<Self>
    {
        unimplemented!();
    }
}
<|endoftext|>


<commit_before>use std::cmp::min;

use netbuf::MAX_BUF_SIZE;
use rotor::Scope;
use rotor_stream::{Protocol, StreamSocket, Deadline, Expectation as E};
use rotor_stream::{Request, Transport};
use hyper::status::StatusCode::{PayloadTooLarge};
use hyper::method::Method::Head;
use hyper::header::Expect;

use super::{MAX_HEADERS_SIZE, MAX_CHUNK_HEAD};
use super::{Response};
use super::protocol::{Server, RecvMode};
use super::context::Context;
use super::request::Head;
use super::body::BodyKind;
use super::ResponseImpl;

struct ReadBody<M: Sized> {
    machine: M,
    deadline: Deadline,
    progress: BodyProgress,
    response: ResponseImpl,
}

pub enum BodyProgress {
    /// Buffered fixed-size request (bytes left)
    BufferFixed(usize),
    /// Buffered request till end of input (byte limit)
    BufferEOF(usize),
    /// Buffered request with chunked encoding
    /// (limit, bytes buffered, bytes left for current chunk)
    BufferChunked(usize, usize, usize),
    /// Progressive fixed-size request (size hint, bytes left)
    ProgressiveFixed(usize, u64),
    /// Progressive till end of input (size hint)
    ProgressiveEOF(usize),
    /// Progressive with chunked encoding (hint, bytes left for current chunk)
    // To be able to merge chunks, should add offset here
    ProgressiveChunked(usize, u64),
}

pub struct Parser<M: Sized>(ParserImpl<M>);

enum ParserImpl<M: Sized> {
    Idle,
    ReadHeaders,
    ReadingBody(ReadBody<M>),
    /// Close connection after buffer is flushed. In other cases -> Idle
    Processing(M, ResponseImpl, Deadline),
    DoneResponse,
}

impl<M> Parser<M>
{
    fn flush<C>(scope: &mut Scope<C>) -> Request<Parser<M>>
        where C: Context
    {
        Some((Parser(ParserImpl::DoneResponse), E::Flush(0),
              Deadline::now() + scope.byte_timeout()))
    }
}

fn start_headers<C: Context, M: Sized>(scope: &mut Scope<C>)
    -> Request<Parser<M>>
{
    Some((Parser(ParserImpl::ReadHeaders),
          E::Delimiter(0, b"\r\n\r\n", MAX_HEADERS_SIZE),
          Deadline::now() + scope.byte_timeout()))
}

fn start_body(mode: RecvMode, body: BodyKind) -> BodyProgress {
    use super::body::BodyKind::*;
    use super::protocol::RecvMode::*;
    use self::BodyProgress::*;

    match (mode, body) {
        // The size of Fixed(x) is checked in parse_headers
        (Buffered(_), Fixed(y)) => BufferFixed(y as usize),
        (Buffered(x), Chunked) => BufferChunked(x, 0, 0),
        (Buffered(x), Eof) => BufferEOF(x),
        (Progressive(x), Fixed(y)) => ProgressiveFixed(x, y),
        (Progressive(x), Chunked) => ProgressiveChunked(x, 0),
        (Progressive(x), Eof) => ProgressiveEOF(x),
        (_, Upgrade) => unimplemented!(),
    }
}

// Parses headers
//
// On error returns bool, which is true if keep-alive connection can be
// carried on.
fn parse_headers<C, M, S>(transport: &mut Transport<S>, end: usize,
    scope: &mut Scope<C>) -> Result<ReadBody<M>, bool>
    where M: Server<C>,
          S: StreamSocket,
          C: Context,
{
    // Determines if we can keep-alive after error response.
    // We may not be able to keep keep-alive for multiple reasons:
    //
    // 1. When request headers are too wrong
    //    (probably client connects with wrong protocol)
    //
    // 2. When request contains non-empty request body (we don't
    //    want to wait until it is uploaded just to send error)
    //
    // Note we definitely can't keep alive if we can't say
    // whether request method is HEAD
    //
    // All of these are important to avoid cache poisoning attacks
    // on proxy servers.
    let mut can_keep_alive = false;
    // Determines if we can safely send the response body
    let mut is_head = false;

    let status = match Head::parse(&transport.input()[..end+4]) {
        Ok(head) => {
            is_head = head.method == Head;
            match M::headers_received(&head, scope) {
                Ok((_, RecvMode::Buffered(x), _)) if x >= MAX_BUF_SIZE
                => panic!("Can't buffer {} bytes, max {}",
                          x, MAX_BUF_SIZE),
                Ok((m, mode, dline)) => {
                    match BodyKind::parse(&head) {
                        Ok(body) => {
                            // TODO(tailhook)
                            // Probably can handle small
                            // request bodies that are already
                            // in the buffer
                            if body == BodyKind::Fixed(0) {
                                can_keep_alive = true;
                            }
                            match (body, mode) {
                                (BodyKind::Fixed(x), RecvMode::Buffered(y))
                                if x >= y as u64 => {
                                    Err(PayloadTooLarge)
                                }
                                _ => {
                                    Ok((head, body, m, mode, dline))
                                }
                            }
                        }
                        Err(status) => Err(status),
                    }
                }
                Err(status) => Err(status),
            }
        }
        Err(status) => Err(status),
    };
    transport.input().consume(end+4);
    match status {
        Ok((head, body, m, mode, dline)) => {
            if head.headers.get::<Expect>() == Some(&Expect::Continue) {
                // Handler has already approved request, so just push it
                transport.output().extend(
                    format!("{} 100 Continue\r\n\r\n", head.version)
                    .as_bytes());
            }
            let mut resp = Response::new(transport.output(), &head);
            let m = match m.request_start(head, &mut resp, scope) {
                Some(m) => m,
                None => {
                    let okay = resp.finish();
                    return Err(okay);
                }
            };
            Ok(ReadBody {
                machine: m,
                deadline: dline,
                progress: start_body(mode, body),
                response: resp.internal(),
            })
        }
        Err(status) => {
            let mut resp = Response::simple(transport.output(), is_head);
            scope.emit_error_page(status, &mut resp);
            let okay = resp.finish();
            Err(can_keep_alive && okay)
        }
    }
}

impl<M> ParserImpl<M>
{
    fn request<C>(self, scope: &mut Scope<C>) -> Request<Parser<M>>
        where C: Context
    {
        use rotor_stream::Expectation::*;
        use self::ParserImpl::*;
        use self::BodyProgress::*;
        let (exp, dline) = match self {
            Idle => (Bytes(0), None),
            ReadHeaders => (Delimiter(0, b"\r\n\r\n", MAX_HEADERS_SIZE), None),
            ReadingBody(ref b) => {
                let exp = match *&b.progress {
                    BufferFixed(x) => Bytes(x),
                    BufferEOF(x) => Eof(x),
                    BufferChunked(_, off, 0)
                    => Delimiter(off, b"\r\n", off+MAX_CHUNK_HEAD),
                    BufferChunked(limit, off, y) => Bytes(off + min(limit, y)),
                    ProgressiveFixed(hint, left)
                    => Bytes(min(hint as u64, left) as usize),
                    ProgressiveEOF(hint) => Eof(hint),
                    ProgressiveChunked(_, 0)
                    => Delimiter(0, b"\r\n", 0+MAX_CHUNK_HEAD),
                    ProgressiveChunked(hint, left)
                    => Bytes(min(hint as u64, left) as usize)
                };
                (exp, Some(b.deadline))
            }
            Processing(..) => unreachable!(),
            /// TODO(tailhook) fix output timeout
            DoneResponse => (Flush(0), None),
        };

        let byte_dline = Deadline::now() + scope.byte_timeout();
        let deadline = dline.map_or_else(
            || byte_dline,
            |x| min(byte_dline, x));
        Some((Parser(self), exp, deadline))
    }
}

impl<C, M, S> Protocol<C, S> for Parser<M>
    where M: Server<C>,
          S: StreamSocket,
          C: Context,
{
    type Seed = ();
    fn create(_seed: (), _sock: &mut S, scope: &mut Scope<C>)
        -> Request<Self>
    {
        Some((Parser(ParserImpl::Idle), E::Bytes(1),
            Deadline::now() + scope.byte_timeout()))
    }
    fn bytes_read(self, transport: &mut Transport<S>,
                  end: usize, scope: &mut Scope<C>)
        -> Request<Self>
    {
        use self::ParserImpl::*;
        use self::BodyProgress::*;
        match self.0 {
            Idle => {
                start_headers(scope)
            }
            ReadHeaders => {
                match parse_headers::<C, M, S>(transport, end, scope) {
                    Ok(body) => {
                        ReadingBody(body).request(scope)
                    }
                    Err(can_keep_alive) => {
                        if can_keep_alive {
                            Idle.request(scope)
                        } else {
                            Parser::flush(scope)
                        }
                    }
                }
            }
            ReadingBody(rb) => {
                let (inp, out) = transport.buffers();
                let mut resp = rb.response.with(out);
                let res = match rb.progress {
                    BufferFixed(x) => {
                        let m = rb.machine.request_received(&inp[..x],
                                                    &mut resp, scope);
                        m.map(|x| (x, None))
                    }
                    BufferEOF(..) => unimplemented!(),
                    BufferChunked(..) => unimplemented!(),
                    ProgressiveFixed(..) => unimplemented!(),
                    ProgressiveEOF(..) => unimplemented!(),
                    ProgressiveChunked(..) => unimplemented!(),
                };
                match res {
                    Some((m, Some(p))) => {
                        ReadingBody(ReadBody {
                            machine: m,
                            deadline: rb.deadline,
                            progress: p,
                            response: resp.internal(),
                        }).request(scope)
                    }
                    Some((m, None)) => {
                        Some((Parser(Processing(
                                    m, resp.internal(), rb.deadline)),
                              E::Sleep, rb.deadline))
                    }
                    None => {
                        Parser::flush(scope)
                    }
                }
            }
            // Spurious event?
            me @ DoneResponse => me.request(scope),
            Processing(m, r, dline) => Some((Parser(Processing(m, r, dline)),
                                             E::Sleep, dline)),
        }
    }
    fn bytes_flushed(self, _transport: &mut Transport<S>,
                     scope: &mut Scope<C>)
        -> Request<Self>
    {
        match self.0 {
            ParserImpl::DoneResponse => None,
            me => me.request(scope),
        }
    }
    fn timeout(self, _transport: &mut Transport<S>,
        _scope: &mut Scope<C>)
        -> Request<Self>
    {
        unimplemented!();
    }
    fn delimiter_not_found(self, _transport: &mut Transport<S>,
        scope: &mut Scope<C>)
        -> Request<Self>
    {
        use self::ParserImpl::*;
        use self::BodyProgress::*;
        // We may just flush and exit in every state. But:
        // 1. The match asserts that we know which state parser may be in
        // 2. We may send more specific response, so that browser will not
        //    retry ugly request multiple times
        match self.0 {
            ReadHeaders
                // TODO(tailhook) send RequestHeaderFieldsTooLarge
            | ReadingBody( ReadBody { progress: ProgressiveChunked(_, 0), ..})
            | ReadingBody( ReadBody { progress: BufferChunked(_, _, 0), ..})
                // TODO(tailhook) send BadRequest ?
            => {
                // Should we flush or just close?
                // Probably closing is useful because previous responses might
                // be absolutely valid, and we've got invalid pipelined
                // request
                Parser::flush(scope)
            }
            // TODO(tailhook) Any other weird cases?
            _ => unreachable!(),
        }
    }
    fn wakeup(self, _transport: &mut Transport<S>, scope: &mut Scope<C>)
        -> Request<Self>
    {
        use self::ParserImpl::*;
        match self.0 {
            me@Idle | me@ReadHeaders | me@DoneResponse => me.request(scope),
            ReadingBody(_reader) => {
                unimplemented!();
            }
            Processing(..) => {
                unimplemented!();
            }
        }
    }
}
<commit_msg>Support of early exit by request handler and keep-alive connections<commit_after>use std::cmp::min;

use netbuf::MAX_BUF_SIZE;
use rotor::Scope;
use rotor_stream::{Protocol, StreamSocket, Deadline, Expectation as E};
use rotor_stream::{Request, Transport};
use hyper::status::StatusCode::{PayloadTooLarge};
use hyper::method::Method::Head;
use hyper::header::Expect;

use super::{MAX_HEADERS_SIZE, MAX_CHUNK_HEAD};
use super::{Response};
use super::protocol::{Server, RecvMode};
use super::context::Context;
use super::request::Head;
use super::body::BodyKind;
use super::ResponseImpl;

struct ReadBody<M: Sized> {
    machine: Option<M>,
    deadline: Deadline,
    progress: BodyProgress,
    response: ResponseImpl,
}

pub enum BodyProgress {
    /// Buffered fixed-size request (bytes left)
    BufferFixed(usize),
    /// Buffered request till end of input (byte limit)
    BufferEOF(usize),
    /// Buffered request with chunked encoding
    /// (limit, bytes buffered, bytes left for current chunk)
    BufferChunked(usize, usize, usize),
    /// Progressive fixed-size request (size hint, bytes left)
    ProgressiveFixed(usize, u64),
    /// Progressive till end of input (size hint)
    ProgressiveEOF(usize),
    /// Progressive with chunked encoding (hint, bytes left for current chunk)
    // To be able to merge chunks, should add offset here
    ProgressiveChunked(usize, u64),
}

pub struct Parser<M: Sized>(ParserImpl<M>);

enum ParserImpl<M: Sized> {
    Idle,
    ReadHeaders,
    ReadingBody(ReadBody<M>),
    /// Close connection after buffer is flushed. In other cases -> Idle
    Processing(M, ResponseImpl, Deadline),
    DoneResponse,
}

impl<M> Parser<M>
{
    fn flush<C>(scope: &mut Scope<C>) -> Request<Parser<M>>
        where C: Context
    {
        Some((Parser(ParserImpl::DoneResponse), E::Flush(0),
              Deadline::now() + scope.byte_timeout()))
    }
}

fn start_headers<C: Context, M: Sized>(scope: &mut Scope<C>)
    -> Request<Parser<M>>
{
    Some((Parser(ParserImpl::ReadHeaders),
          E::Delimiter(0, b"\r\n\r\n", MAX_HEADERS_SIZE),
          Deadline::now() + scope.byte_timeout()))
}

fn start_body(mode: RecvMode, body: BodyKind) -> BodyProgress {
    use super::body::BodyKind::*;
    use super::protocol::RecvMode::*;
    use self::BodyProgress::*;

    match (mode, body) {
        // The size of Fixed(x) is checked in parse_headers
        (Buffered(_), Fixed(y)) => BufferFixed(y as usize),
        (Buffered(x), Chunked) => BufferChunked(x, 0, 0),
        (Buffered(x), Eof) => BufferEOF(x),
        (Progressive(x), Fixed(y)) => ProgressiveFixed(x, y),
        (Progressive(x), Chunked) => ProgressiveChunked(x, 0),
        (Progressive(x), Eof) => ProgressiveEOF(x),
        (_, Upgrade) => unimplemented!(),
    }
}

// Parses headers
//
// On error returns bool, which is true if keep-alive connection can be
// carried on.
fn parse_headers<C, M, S>(transport: &mut Transport<S>, end: usize,
    scope: &mut Scope<C>) -> Result<ReadBody<M>, bool>
    where M: Server<C>,
          S: StreamSocket,
          C: Context,
{
    // Determines if we can keep-alive after error response.
    // We may not be able to keep keep-alive for multiple reasons:
    //
    // 1. When request headers are too wrong
    //    (probably client connects with wrong protocol)
    //
    // 2. When request contains non-empty request body (we don't
    //    want to wait until it is uploaded just to send error)
    //
    // Note we definitely can't keep alive if we can't say
    // whether request method is HEAD
    //
    // All of these are important to avoid cache poisoning attacks
    // on proxy servers.
    let mut can_keep_alive = false;
    // Determines if we can safely send the response body
    let mut is_head = false;

    let status = match Head::parse(&transport.input()[..end+4]) {
        Ok(head) => {
            is_head = head.method == Head;
            match M::headers_received(&head, scope) {
                Ok((_, RecvMode::Buffered(x), _)) if x >= MAX_BUF_SIZE
                => panic!("Can't buffer {} bytes, max {}",
                          x, MAX_BUF_SIZE),
                Ok((m, mode, dline)) => {
                    match BodyKind::parse(&head) {
                        Ok(body) => {
                            // TODO(tailhook)
                            // Probably can handle small
                            // request bodies that are already
                            // in the buffer
                            if body == BodyKind::Fixed(0) {
                                can_keep_alive = true;
                            }
                            match (body, mode) {
                                (BodyKind::Fixed(x), RecvMode::Buffered(y))
                                if x >= y as u64 => {
                                    Err(PayloadTooLarge)
                                }
                                _ => {
                                    Ok((head, body, m, mode, dline))
                                }
                            }
                        }
                        Err(status) => Err(status),
                    }
                }
                Err(status) => Err(status),
            }
        }
        Err(status) => Err(status),
    };
    transport.input().consume(end+4);
    match status {
        Ok((head, body, m, mode, dline)) => {
            if head.headers.get::<Expect>() == Some(&Expect::Continue) {
                // Handler has already approved request, so just push it
                transport.output().extend(
                    format!("{} 100 Continue\r\n\r\n", head.version)
                    .as_bytes());
            }
            let mut resp = Response::new(transport.output(), &head);
            Ok(ReadBody {
                machine: m.request_start(head, &mut resp, scope),
                deadline: dline,
                progress: start_body(mode, body),
                response: resp.internal(),
            })
        }
        Err(status) => {
            let mut resp = Response::simple(transport.output(), is_head);
            scope.emit_error_page(status, &mut resp);
            let okay = resp.finish();
            Err(can_keep_alive && okay)
        }
    }
}

impl<M> ParserImpl<M>
{
    fn request<C>(self, scope: &mut Scope<C>) -> Request<Parser<M>>
        where C: Context
    {
        use rotor_stream::Expectation::*;
        use self::ParserImpl::*;
        use self::BodyProgress::*;
        let (exp, dline) = match self {
            Idle => (Bytes(0), None),
            ReadHeaders => (Delimiter(0, b"\r\n\r\n", MAX_HEADERS_SIZE), None),
            ReadingBody(ref b) => {
                let exp = match *&b.progress {
                    BufferFixed(x) => Bytes(x),
                    BufferEOF(x) => Eof(x),
                    BufferChunked(_, off, 0)
                    => Delimiter(off, b"\r\n", off+MAX_CHUNK_HEAD),
                    BufferChunked(limit, off, y) => Bytes(off + min(limit, y)),
                    ProgressiveFixed(hint, left)
                    => Bytes(min(hint as u64, left) as usize),
                    ProgressiveEOF(hint) => Eof(hint),
                    ProgressiveChunked(_, 0)
                    => Delimiter(0, b"\r\n", 0+MAX_CHUNK_HEAD),
                    ProgressiveChunked(hint, left)
                    => Bytes(min(hint as u64, left) as usize)
                };
                (exp, Some(b.deadline))
            }
            Processing(..) => unreachable!(),
            /// TODO(tailhook) fix output timeout
            DoneResponse => (Flush(0), None),
        };

        let byte_dline = Deadline::now() + scope.byte_timeout();
        let deadline = dline.map_or_else(
            || byte_dline,
            |x| min(byte_dline, x));
        Some((Parser(self), exp, deadline))
    }
}

impl<C, M, S> Protocol<C, S> for Parser<M>
    where M: Server<C>,
          S: StreamSocket,
          C: Context,
{
    type Seed = ();
    fn create(_seed: (), _sock: &mut S, scope: &mut Scope<C>)
        -> Request<Self>
    {
        Some((Parser(ParserImpl::Idle), E::Bytes(1),
            Deadline::now() + scope.byte_timeout()))
    }
    fn bytes_read(self, transport: &mut Transport<S>,
                  end: usize, scope: &mut Scope<C>)
        -> Request<Self>
    {
        use self::ParserImpl::*;
        use self::BodyProgress::*;
        match self.0 {
            Idle => {
                start_headers(scope)
            }
            ReadHeaders => {
                match parse_headers::<C, M, S>(transport, end, scope) {
                    Ok(body) => {
                        ReadingBody(body).request(scope)
                    }
                    Err(can_keep_alive) => {
                        if can_keep_alive {
                            Idle.request(scope)
                        } else {
                            Parser::flush(scope)
                        }
                    }
                }
            }
            ReadingBody(rb) => {
                let (inp, out) = transport.buffers();
                let mut resp = rb.response.with(out);
                let (m, progress) = match rb.progress {
                    BufferFixed(x) => {
                        let m = rb.machine
                            .and_then(|m| m.request_received(
                                            &inp[..x], &mut resp, scope));
                        (m, None)
                    }
                    BufferEOF(..) => unimplemented!(),
                    BufferChunked(..) => unimplemented!(),
                    ProgressiveFixed(..) => unimplemented!(),
                    ProgressiveEOF(..) => unimplemented!(),
                    ProgressiveChunked(..) => unimplemented!(),
                };
                match progress {
                    Some(p) => {
                        ReadingBody(ReadBody {
                            machine: m,
                            deadline: rb.deadline,
                            progress: p,
                            response: resp.internal(),
                        }).request(scope)
                    }
                    None => match m {
                        Some(m) => {
                            Some((Parser(Processing(
                                        m, resp.internal(), rb.deadline)),
                                 E::Sleep, rb.deadline))
                        }
                        None => {
                            Idle.request(scope)
                        }
                    }
                }
            }
            // Spurious event?
            me @ DoneResponse => me.request(scope),
            Processing(m, r, dline) => Some((Parser(Processing(m, r, dline)),
                                             E::Sleep, dline)),
        }
    }
    fn bytes_flushed(self, _transport: &mut Transport<S>,
                     scope: &mut Scope<C>)
        -> Request<Self>
    {
        match self.0 {
            ParserImpl::DoneResponse => None,
            me => me.request(scope),
        }
    }
    fn timeout(self, _transport: &mut Transport<S>,
        _scope: &mut Scope<C>)
        -> Request<Self>
    {
        unimplemented!();
    }
    fn delimiter_not_found(self, _transport: &mut Transport<S>,
        scope: &mut Scope<C>)
        -> Request<Self>
    {
        use self::ParserImpl::*;
        use self::BodyProgress::*;
        // We may just flush and exit in every state. But:
        // 1. The match asserts that we know which state parser may be in
        // 2. We may send more specific response, so that browser will not
        //    retry ugly request multiple times
        match self.0 {
            ReadHeaders
                // TODO(tailhook) send RequestHeaderFieldsTooLarge
            | ReadingBody( ReadBody { progress: ProgressiveChunked(_, 0), ..})
            | ReadingBody( ReadBody { progress: BufferChunked(_, _, 0), ..})
                // TODO(tailhook) send BadRequest ?
            => {
                // Should we flush or just close?
                // Probably closing is useful because previous responses might
                // be absolutely valid, and we've got invalid pipelined
                // request
                Parser::flush(scope)
            }
            // TODO(tailhook) Any other weird cases?
            _ => unreachable!(),
        }
    }
    fn wakeup(self, _transport: &mut Transport<S>, scope: &mut Scope<C>)
        -> Request<Self>
    {
        use self::ParserImpl::*;
        match self.0 {
            me@Idle | me@ReadHeaders | me@DoneResponse => me.request(scope),
            ReadingBody(_reader) => {
                unimplemented!();
            }
            Processing(..) => {
                unimplemented!();
            }
        }
    }
}
<|endoftext|>