<commit_before>extern crate time;
extern crate hawk;
extern crate hyper;
extern crate hyper_hawk;
extern crate url;

use hawk::{Request, Credentials, Key, SHA256, PayloadHasher};
use std::io::{Read, Write};
use hyper_hawk::{HawkScheme, ServerAuthorization};
use hyper::Client;
use hyper::header;
use hyper::server;
use url::Url;
use std::thread;

fn client(send_hash: bool, require_hash: bool, port: u16) {
    let credentials = Credentials {
        id: "test-client".to_string(),
        key: Key::new(vec![1u8; 32], &SHA256),
    };
    let url = Url::parse(&format!("http://localhost:{}/resource", port)).unwrap();
    let body = "foo=bar";

    let payload_hash;
    let mut request = Request::new().method("POST").url(&url).unwrap();

    if send_hash {
        payload_hash = PayloadHasher::hash("text/plain".as_bytes(), &SHA256, body.as_bytes());
        request = request.hash(Some(&payload_hash));
    }

    let mut headers = hyper::header::Headers::new();
    let header = request.make_header(&credentials).unwrap();
    headers.set(header::Authorization(HawkScheme(header.clone())));

    let client = Client::new();
    let mut res = client
        .post(url.as_str())
        .headers(headers)
        .body(body)
        .send()
        .unwrap();

    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    assert_eq!(res.status, hyper::Ok);
    assert_eq!(body, "OK");

    let server_hdr: &ServerAuthorization<HawkScheme> = res.headers.get().unwrap();

    // most fields in `Server-Authorization: Hawk` are omitted
    assert_eq!(server_hdr.id, None);
    assert_eq!(server_hdr.ts, None);
    assert_eq!(server_hdr.nonce, None);
    assert_eq!(server_hdr.ext, Some("server-ext".to_string()));
    if require_hash {
        assert!(server_hdr.hash.is_some());
    }
    assert_eq!(server_hdr.app, None);
    assert_eq!(server_hdr.dlg, None);

    let payload_hash;
    let mut response = request.make_response(&header);
    if require_hash {
        payload_hash = PayloadHasher::hash("text/plain".as_bytes(), &SHA256, body.as_bytes());
        response = response.hash(&payload_hash);
    }

    if !response.validate_header(&server_hdr, &credentials.key) {
        panic!("authentication of response header failed");
    }
}

struct TestHandler {
    require_hash: bool,
    send_hash: bool,
    port: u16,
}

impl server::Handler for TestHandler {
    fn handle(&self, mut req: server::Request, mut res: server::Response) {
        // get the body
        let mut body = String::new();
        req.read_to_string(&mut body).unwrap();

        // get the Authorization header the client sent
        let hdr: &header::Authorization<HawkScheme> = req.headers.get().unwrap();

        // build a request object based on what we know
        let payload_hash;
        let mut request = Request::new()
            .method("POST")
            .host("localhost")
            .port(self.port)
            .path("/resource");

        // add a body hash, if we require such a thing
        if self.require_hash {
            payload_hash = PayloadHasher::hash("text/plain".as_bytes(), &SHA256, body.as_bytes());
            request = request.hash(Some(&payload_hash));
        }

        assert_eq!(hdr.id, Some("test-client".to_string()));
        assert_eq!(hdr.ext, None);
        let key = Key::new(vec![1u8; 32], &SHA256);
        if !request.validate_header(&hdr, &key, time::Duration::minutes(1)) {
            panic!("header validation failed");
        }

        let body = "OK".as_bytes();
        let payload_hash;
        let mut response = request.make_response(&hdr).ext("server-ext");
        if self.send_hash {
            payload_hash = PayloadHasher::hash("text/plain".as_bytes(), &SHA256, body);
            response = response.hash(&payload_hash);
        }
        let server_hdr = response.make_header(&key).unwrap();
        res.headers_mut()
            .set(ServerAuthorization(HawkScheme(server_hdr)));

        res.headers_mut()
            .set(header::ContentLength(body.len() as u64));

        let mut res = res.start().unwrap();
        res.write_all(body).unwrap();
    }
}

fn run_client_server(client_send_hash: bool,
                     server_require_hash: bool,
                     server_send_hash: bool,
                     client_require_hash: bool,
                     port: u16) {
    let handler = TestHandler {
        require_hash: server_require_hash,
        send_hash: server_send_hash,
        port: port,
    };
    let server = server::Server::http(("127.0.0.1", port)).unwrap();
    let mut listening = server.handle_threads(handler, 1).unwrap();
    let client_thread =
        thread::spawn(move || { client(client_send_hash, client_require_hash, port); });

    // finish both threads
    let client_res = client_thread.join();
    listening.close().unwrap();

    // *then* evaluate client_res
    if let Err(_) = client_res {
        panic!("client failed");
    }
}

// Each test uses a different port, since the tests run in parallel.

#[test]
fn no_hashes() {
    run_client_server(false, false, false, false, 9001);
}

#[test]
fn client_sends() {
    run_client_server(true, false, false, false, 9002);
}

#[test]
fn server_requires() {
    run_client_server(true, true, false, false, 9003);
}

#[test]
fn server_sends() {
    run_client_server(true, true, true, false, 9004);
}

#[test]
fn client_requires() {
    run_client_server(true, true, true, true, 9005);
}

#[test]
fn response_hash_only() {
    run_client_server(false, false, true, true, 9006);
}<commit_msg>use dynamically assigned ports for client-server tests<commit_after>extern crate time;
extern crate hawk;
extern crate hyper;
extern crate hyper_hawk;
extern crate url;

use hawk::{Request, Credentials, Key, SHA256, PayloadHasher};
use std::io::{Read, Write};
use hyper_hawk::{HawkScheme, ServerAuthorization};
use hyper::Client;
use hyper::header;
use hyper::server;
use url::Url;
use std::thread;

fn client(send_hash: bool, require_hash: bool, port: u16) {
    let credentials = Credentials {
        id: "test-client".to_string(),
        key: Key::new(vec![1u8; 32], &SHA256),
    };
    let url = Url::parse(&format!("http://localhost:{}/resource", port)).unwrap();
    let body = "foo=bar";

    let payload_hash;
    let mut request = Request::new().method("POST").url(&url).unwrap();
    // for purposes of the test, we pretend we're using port 9999
    request = request.port(9999);

    if send_hash {
        payload_hash = PayloadHasher::hash("text/plain".as_bytes(), &SHA256, body.as_bytes());
        request = request.hash(Some(&payload_hash));
    }

    let mut headers = hyper::header::Headers::new();
    let header = request.make_header(&credentials).unwrap();
    headers.set(header::Authorization(HawkScheme(header.clone())));

    let client = Client::new();
    let mut res = client
        .post(url.as_str())
        .headers(headers)
        .body(body)
        .send()
        .unwrap();

    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();
    assert_eq!(res.status, hyper::Ok);
    assert_eq!(body, "OK");

    let server_hdr: &ServerAuthorization<HawkScheme> = res.headers.get().unwrap();

    // most fields in `Server-Authorization: Hawk` are omitted
    assert_eq!(server_hdr.id, None);
    assert_eq!(server_hdr.ts, None);
    assert_eq!(server_hdr.nonce, None);
    assert_eq!(server_hdr.ext, Some("server-ext".to_string()));
    if require_hash {
        assert!(server_hdr.hash.is_some());
    }
    assert_eq!(server_hdr.app, None);
    assert_eq!(server_hdr.dlg, None);

    let payload_hash;
    let mut response = request.make_response(&header);
    if require_hash {
        payload_hash = PayloadHasher::hash("text/plain".as_bytes(), &SHA256, body.as_bytes());
        response = response.hash(&payload_hash);
    }

    if !response.validate_header(&server_hdr, &credentials.key) {
        panic!("authentication of response header failed");
    }
}

struct TestHandler {
    require_hash: bool,
    send_hash: bool,
}

impl server::Handler for TestHandler {
    fn handle(&self, mut req: server::Request, mut res: server::Response) {
        // get the body
        let mut body = String::new();
        req.read_to_string(&mut body).unwrap();

        // get the Authorization header the client sent
        let hdr: &header::Authorization<HawkScheme> = req.headers.get().unwrap();

        // build a request object based on what we know
        let payload_hash;
        let mut request = Request::new()
            .method("POST")
            .host("localhost")
            .port(9999)
            .path("/resource");

        // add a body hash, if we require such a thing
        if self.require_hash {
            payload_hash = PayloadHasher::hash("text/plain".as_bytes(), &SHA256, body.as_bytes());
            request = request.hash(Some(&payload_hash));
        }

        assert_eq!(hdr.id, Some("test-client".to_string()));
        assert_eq!(hdr.ext, None);
        let key = Key::new(vec![1u8; 32], &SHA256);
        if !request.validate_header(&hdr, &key, time::Duration::minutes(1)) {
            panic!("header validation failed");
        }

        let body = "OK".as_bytes();
        let payload_hash;
        let mut response = request.make_response(&hdr).ext("server-ext");
        if self.send_hash {
            payload_hash = PayloadHasher::hash("text/plain".as_bytes(), &SHA256, body);
            response = response.hash(&payload_hash);
        }
        let server_hdr = response.make_header(&key).unwrap();
        res.headers_mut()
            .set(ServerAuthorization(HawkScheme(server_hdr)));

        res.headers_mut()
            .set(header::ContentLength(body.len() as u64));

        let mut res = res.start().unwrap();
        res.write_all(body).unwrap();
    }
}

fn run_client_server(client_send_hash: bool,
                     server_require_hash: bool,
                     server_send_hash: bool,
                     client_require_hash: bool) {
    let handler = TestHandler {
        require_hash: server_require_hash,
        send_hash: server_send_hash,
    };
    let mut server = server::Server::http(("127.0.0.1", 0)).unwrap();
    let local_address = server.local_addr().unwrap();
    let mut listening = server.handle_threads(handler, 1).unwrap();
    let client_thread =
        thread::spawn(move || {
                          client(client_send_hash, client_require_hash, local_address.port());
                      });

    // finish both threads
    let client_res = client_thread.join();
    listening.close().unwrap();

    // *then* evaluate client_res
    if let Err(_) = client_res {
        panic!("client failed");
    }
}

#[test]
fn no_hashes() {
    run_client_server(false, false, false, false);
}

#[test]
fn client_sends() {
    run_client_server(true, false, false, false);
}

#[test]
fn server_requires() {
    run_client_server(true, true, false, false);
}

#[test]
fn server_sends() {
    run_client_server(true, true, true, false);
}

#[test]
fn client_requires() {
    run_client_server(true, true, true, true);
}

#[test]
fn response_hash_only() {
    run_client_server(false, false, true, true);
}
{'fraction_non_alphanumeric': 0.14499465621660135, 'fraction_numerical': 0.010954755967224795, 'mean_word_length': 2.9580542826929856, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4717064', 'n_tokens_mistral': 3544, 'n_tokens_neox': 3465, 'n_words': 773}