<commit_before>//! Handle http requests
//!
//! This implementation is stolen from <https://github.com/getsentry/sentry-cli> and adapted for
//! gerrit server. Thanks Armin!
//!
//! **NOTICE**: The complete call request could be handled with hyper or something like that. This
//! could beused instead the curl-approach.

use curl;
use error::GGRResult;
use std::cell::RefCell;
use std::cell::RefMut;
use std::fmt;
use std::io::Read;
use std::io::Write;
use url;

/// helper function for Request without body content and with content
fn send_req<W: Write>(handle: &mut curl::easy::Easy,
                     out: &mut W,
                     body: Option<Vec<u8>>)
    -> GGRResult<(u32, Vec<String>)> {
        match body {
            Some(body) => {
                let mut body = &body[..];
                try!{handle.upload(true)};
                try!{handle.in_filesize(body.len() as u64)};
                handle_req(handle, out, &mut |buf| body.read(buf).unwrap_or(0))
            },
            None => {
                handle_req(handle, out, &mut |_| 0)
            }
        }
}

/// Does the real send
fn handle_req<W: Write>(handle: &mut curl::easy::Easy,
                       out: &mut W,
                       read: &mut FnMut(&mut [u8]) -> usize)
    -> GGRResult<(u32, Vec<String>)> {
        let mut headers = Vec::new();

        {
            let mut handle = handle.transfer();

            try!{handle.read_function(|buf| Ok(read(buf)))};
            try!{handle.write_function(|data| {
                Ok(match out.write_all(data) {
                    Ok(_) => data.len(),
                    Err(_) => 0,
                })
            })};

            try!{handle.header_function(|data| {
                headers.push(String::from_utf8_lossy(data).into_owned());
                true
            })};

            try!{handle.perform()};
        }

        Ok((try!{handle.response_code()}, headers))
}

/// HTTP methods
pub enum CallMethod {
    /// Identify a GET call
    Get,
}

/// Transform CallMethod type to String
impl fmt::Display for CallMethod {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CallMethod::Get => write!(f, "GET"),
        }
    }
}

/// Represents a HTTP request
///
/// For usage we fill the CallRequest with an header and a body. Later we call via `handler` the
/// gerrit server.
pub struct CallRequest<'a> {
    handle: RefMut<'a, curl::easy::Easy>,
    headers: curl::easy::List,
    body: Option<Vec<u8>>,
}

impl<'a> CallRequest<'a> {
    /// creates new instance of [`CallRequest`] with a curl-handler, the HTTP method and the url
    /// endpoint
    pub fn new(mut handle: RefMut<'a, curl::easy::Easy>,
               method: CallMethod,
               url: String)
        -> GGRResult<CallRequest<'a>> {
            let mut headers = curl::easy::List::new();

            // we only want compact json data
            let _ = headers.append("Accept: application/json");

            match method {
                CallMethod::Get => try!{handle.get(true)},
            };

            try!{handle.url(&url)};

            Ok(CallRequest {
                handle: handle,
                headers: headers,
                body: None,
            })
    }

    /// helper function to handle a request, write the returned content to a `Write` capabel object
    /// and returns a `CallResponse`.
    fn send_into<W:Write>(mut self, out: &mut W) -> GGRResult<CallResponse> {
        try!{self.handle.http_headers(self.headers)};

        let (status, headers) = try!{send_req(&mut self.handle, out, self.body)};

        Ok(CallResponse {
            status: status,
            headers: headers,
            body: None,
        })
    }

    /// Does the send function.
    ///
    /// **NOTICE**: The first 4 characters are cutted from the returned content. We want only json
    /// data which has a prevention against XSSI attacks. More here:
    /// <https://gerrit-documentation.storage.googleapis.com/Documentation/2.12.3/rest-api.html#output>
    pub fn send(self) -> GGRResult<CallResponse> {
        let mut out = Vec::new();
        let mut rv = try!{self.send_into(&mut out)};

        /* cut first 4 bytes from output stream */
        if out.starts_with(b")]}'") {
            out = out[4..].into();
        }

        rv.body = Some(out);
        Ok(rv)
    }
}

/// Representation of a Response
#[derive(Debug)]
pub struct CallResponse {
    /// http return status
    pub status: u32,
    /// header of http response
    pub headers: Vec<String>,
    /// the content
    pub body: Option<Vec<u8>>,
}

/// Abstraction of a HTTP call
pub struct Call {
    url: url::Url,<commit_msg>call: replace try!{...} with try!(...)<commit_after>//! Handle http requests
//!
//! This implementation is stolen from <https://github.com/getsentry/sentry-cli> and adapted for
//! gerrit server. Thanks Armin!
//!
//! **NOTICE**: The complete call request could be handled with hyper or something like that. This
//! could beused instead the curl-approach.

use curl;
use error::GGRResult;
use std::cell::RefCell;
use std::cell::RefMut;
use std::fmt;
use std::io::Read;
use std::io::Write;
use url;

/// helper function for Request without body content and with content
fn send_req<W: Write>(handle: &mut curl::easy::Easy,
                     out: &mut W,
                     body: Option<Vec<u8>>)
    -> GGRResult<(u32, Vec<String>)> {
        match body {
            Some(body) => {
                let mut body = &body[..];
                try!(handle.upload(true));
                try!(handle.in_filesize(body.len() as u64));
                handle_req(handle, out, &mut |buf| body.read(buf).unwrap_or(0))
            },
            None => {
                handle_req(handle, out, &mut |_| 0)
            }
        }
}

/// Does the real send
fn handle_req<W: Write>(handle: &mut curl::easy::Easy,
                       out: &mut W,
                       read: &mut FnMut(&mut [u8]) -> usize)
    -> GGRResult<(u32, Vec<String>)> {
        let mut headers = Vec::new();

        {
            let mut handle = handle.transfer();

            try!(handle.read_function(|buf| Ok(read(buf))));
            try!{handle.write_function(|data| {
                Ok(match out.write_all(data) {
                    Ok(_) => data.len(),
                    Err(_) => 0,
                })
            })};

            try!{handle.header_function(|data| {
                headers.push(String::from_utf8_lossy(data).into_owned());
                true
            })};

            try!(handle.perform());
        }

        Ok((try!{handle.response_code()}, headers))
}

/// HTTP methods
pub enum CallMethod {
    /// Identify a GET call
    Get,
}

/// Transform CallMethod type to String
impl fmt::Display for CallMethod {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CallMethod::Get => write!(f, "GET"),
        }
    }
}

/// Represents a HTTP request
///
/// For usage we fill the CallRequest with an header and a body. Later we call via `handler` the
/// gerrit server.
pub struct CallRequest<'a> {
    handle: RefMut<'a, curl::easy::Easy>,
    headers: curl::easy::List,
    body: Option<Vec<u8>>,
}

impl<'a> CallRequest<'a> {
    /// creates new instance of [`CallRequest`] with a curl-handler, the HTTP method and the url
    /// endpoint
    pub fn new(mut handle: RefMut<'a, curl::easy::Easy>,
               method: CallMethod,
               url: String)
        -> GGRResult<CallRequest<'a>> {
            let mut headers = curl::easy::List::new();

            // we only want compact json data
            let _ = headers.append("Accept: application/json");

            match method {
                CallMethod::Get => try!{handle.get(true)},
            };

            try!(handle.url(&url));

            Ok(CallRequest {
                handle: handle,
                headers: headers,
                body: None,
            })
    }

    /// helper function to handle a request, write the returned content to a `Write` capabel object
    /// and returns a `CallResponse`.
    fn send_into<W:Write>(mut self, out: &mut W) -> GGRResult<CallResponse> {
        try!(self.handle.http_headers(self.headers));

        let (status, headers) = try!(send_req(&mut self.handle, out, self.body));

        Ok(CallResponse {
            status: status,
            headers: headers,
            body: None,
        })
    }

    /// Does the send function.
    ///
    /// **NOTICE**: The first 4 characters are cutted from the returned content. We want only json
    /// data which has a prevention against XSSI attacks. More here:
    /// <https://gerrit-documentation.storage.googleapis.com/Documentation/2.12.3/rest-api.html#output>
    pub fn send(self) -> GGRResult<CallResponse> {
        let mut out = Vec::new();
        let mut rv = try!(self.send_into(&mut out));

        /* cut first 4 bytes from output stream */
        if out.starts_with(b")]}'") {
            out = out[4..].into();
        }

        rv.body = Some(out);
        Ok(rv)
    }
}

/// Representation of a Response
#[derive(Debug)]
pub struct CallResponse {
    /// http return status
    pub status: u32,
    /// header of http response
    pub headers: Vec<String>,
    /// the content
    pub body: Option<Vec<u8>>,
}

/// Abstraction of a HTTP call
pub struct Call {
    url: url::Url,
{'fraction_non_alphanumeric': 0.14421754311713048, 'fraction_numerical': 0.004867209819066766, 'mean_word_length': 1.9254100897554935, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 79, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, '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': '2810740', 'n_tokens_mistral': 2851, 'n_tokens_neox': 2739, 'n_words': 781}