1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! HTTP client wrapper
//!
//! This module contains `WebHandle` which is a simple wrapper over [Client](https://docs.rs/reqwest/0.10.1/reqwest/struct.Client.html)
//! used to communicate with a web server without needing numerous parameters

use std::ops::Deref;
use reqwest::{
    Client,
    Method,
    header::HeaderMap,
    header::*,
    Response,
};
use lazy_static::lazy_static;

lazy_static! {
    static ref DEFAULT_HEADERS: HeaderMap<HeaderValue> = {
        let mut hm = HeaderMap::with_capacity(3);
        hm.insert(CONTENT_TYPE, "application/x-www-form-urlencoded".parse().unwrap());
        hm.insert(ACCEPT_ENCODING, "gzip".parse().unwrap());
        hm.insert(USER_AGENT, "ksoap2-android/2.6.0+".parse().unwrap());

        hm
    };
}

/// Struct which manages and sends web requests asynchronously
#[derive(Clone, Copy)]
pub struct WebHandle;

impl WebHandle {
    /// Asynchronously sends a HTTP Request requiring manual parameters which returns a [Response](https://docs.rs/reqwest/0.10.1/reqwest/struct.Response.html)
    ///
    /// # Example
    ///
    /// ```
    /// use studentvue::request::WebHandle;
    /// use reqwest::{
    ///     header::HeaderMap,
    ///     Method,
    /// };
    ///
    /// #[tokio::main]
    /// async fn main() -> Result<(), reqwest::Error> {
    ///     let params: Vec<&str> = Vec::new();
    ///     let req = WebHandle::make_web_request("https://www.google.com", Method::POST, params, &HeaderMap::new())
    ///         .await?;
    ///
    ///     println!("{:?}", req.status());
    ///     Ok(())
    /// }
    /// ```
    ///
    #[inline]
    pub async fn make_web_request<R, M, S>(uri: R, method: M, params: S, headers: &HeaderMap) -> Result<Response, reqwest::Error>
    where
        R: AsRef<str>,
        M: Into<Method>,
        S: serde::Serialize
    {
        let client = Client::new();
        let request = client
            .request(method.into(), uri.as_ref())
            .headers(headers.clone())
            .form(&params)
            .send()
            .await?;

        Ok(request)
    }

    /// Asynchronously sends a POST request to the corresponding WebService endpoint or an optional url with specified parameters
    ///
    /// # Example
    ///
    /// ```
    /// use studentvue::request::WebHandle;
    ///
    /// #[tokio::main]
    /// async fn main() -> Result<(), reqwest::Error> {
    ///     let res = WebHandle::send("https://afsd.edupoint.com/Service/PXPCommunication.asmx/ProcessWebServiceRequest", &[("key", "value")])
    ///         .await?;
    ///
    ///     Ok(())
    /// }
    /// ```
    ///
    pub async fn send(uri: impl AsRef<str>, params: impl serde::Serialize) -> Result<String, reqwest::Error> {
        let req = WebHandle::make_web_request(uri, Method::POST, params, DEFAULT_HEADERS.deref())
            .await?
            .text()
            .await?;

        Ok(
            String::from_utf8_lossy(req.as_bytes())
                .replace("&lt;", "<")
                .replace("&gt;", ">")
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn status_check() {
        let status = WebHandle::make_web_request("http://www.google.com", Method::GET, <Vec<&str>>::new(), &HeaderMap::new())
            .await
            .unwrap()
            .status();

        assert_eq!(status.as_str(), "200");
    }
}