use reqwest::blocking::{Client, Request};
use reqwest::Url;
use crate::error::SelfError;
use crate::keypair::KeyPair;
pub struct Rest {
client: reqwest::blocking::Client,
identity: String,
signing_key: KeyPair,
}
pub struct Response {
pub code: u16,
pub data: Vec<u8>,
}
impl Rest {
pub fn new(identity: &str, signing_key: KeyPair) -> Rest {
return Rest {
client: Client::new(),
identity: String::from(identity),
signing_key: signing_key,
};
}
pub fn get(&self, url: &str) -> Result<Response, SelfError> {
return self.request(reqwest::Method::GET, url, None);
}
pub fn post(&self, url: &str, body: Vec<u8>) -> Result<Response, SelfError> {
return self.request(reqwest::Method::POST, url, Some(body));
}
pub fn put(&self, url: &str, body: Vec<u8>) -> Result<Response, SelfError> {
return self.request(reqwest::Method::PUT, url, Some(body));
}
pub fn delete(&self, url: &str) -> Result<Response, SelfError> {
return self.request(reqwest::Method::DELETE, url, None);
}
fn authorization(&self, headers: &mut reqwest::header::HeaderMap) {
let mut token = crate::message::Message::new(
"auth.token",
&self.identity,
&self.identity,
Some(std::time::Duration::from_secs(30)),
true,
);
token.sign(&self.signing_key).expect("wont fail");
let jwt = token.to_jwt().unwrap();
let authorization = reqwest::header::HeaderValue::from_str(&jwt);
headers.insert("Authorization", authorization.unwrap());
}
fn request(
&self,
method: reqwest::Method,
url: &str,
body: Option<Vec<u8>>,
) -> Result<Response, SelfError> {
let target = match Url::parse(url) {
Ok(target) => target,
Err(err) => {
println!("{:?}", err);
return Err(SelfError::RestRequestURLInvalid);
}
};
let mut request = Request::new(method, target);
self.authorization(request.headers_mut());
if body.is_some() {
*request.body_mut() = Some(reqwest::blocking::Body::from(body.unwrap()));
}
let response = self.client.execute(request);
if response.is_err() {
return Err(handle_error(response.err().unwrap()));
}
let successful_response = response.unwrap();
let status = successful_response.status().as_u16();
return match successful_response.bytes() {
Ok(bytes) => Ok(Response {
code: status,
data: bytes.to_vec(),
}),
Err(err) => {
println!("{}", err);
return Err(SelfError::RestRequestInvalid);
}
};
}
}
fn handle_error(e: reqwest::Error) -> SelfError {
println!("reqwest err: {}", e);
if e.is_redirect() {
return SelfError::RestRequestRedirected;
}
if e.is_connect() {
return SelfError::RestRequestConnectionFailed;
}
if e.is_timeout() {
return SelfError::RestRequestConnectionTimeout;
}
return SelfError::RestRequestUnknown;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::keypair::{KeyPair, KeyPairType};
use httptest::{matchers::*, responders::*, Expectation, Server};
#[test]
fn get() {
let server = Server::run();
let m = all_of![
request::method_path("GET", "/v1/identities"),
request::headers(contains(key("authorization"))),
];
server.expect(
Expectation::matching(m)
.respond_with(status_code(200).body("{\"status\":\"success\"}")),
);
let kp = KeyPair::new(KeyPairType::Ed25519);
let client = Rest::new("my-self-identity", kp);
let url = server.url_str("/v1/identities");
let response = client.get(&url);
assert!(response.is_ok());
let successful_response = response.unwrap();
assert_eq!(successful_response.code, 200);
assert_eq!(
successful_response.data,
"{\"status\":\"success\"}".as_bytes()
);
}
#[test]
fn post() {
let server = Server::run();
let m = all_of![
request::method_path("POST", "/v1/identities"),
request::headers(contains(key("authorization"))),
request::body("{\"history\":[]\"}"),
];
server.expect(
Expectation::matching(m)
.respond_with(status_code(201).body("{\"status\":\"success\"}")),
);
let kp = KeyPair::new(KeyPairType::Ed25519);
let client = Rest::new("my-self-identity", kp);
let url = server.url_str("/v1/identities");
let response = client.post(&url, "{\"history\":[]\"}".as_bytes().to_vec());
assert!(response.is_ok());
let successful_response = response.unwrap();
assert_eq!(successful_response.code, 201);
assert_eq!(
successful_response.data,
"{\"status\":\"success\"}".as_bytes()
);
}
#[test]
fn put() {
let server = Server::run();
let m = all_of![
request::method_path("PUT", "/v1/identities"),
request::headers(contains(key("authorization"))),
request::body("{\"history\":[]\"}"),
];
server.expect(
Expectation::matching(m)
.respond_with(status_code(202).body("{\"status\":\"success\"}")),
);
let kp = KeyPair::new(KeyPairType::Ed25519);
let client = Rest::new("my-self-identity", kp);
let url = server.url_str("/v1/identities");
let response = client.put(&url, "{\"history\":[]\"}".as_bytes().to_vec());
assert!(response.is_ok());
let successful_response = response.unwrap();
assert_eq!(successful_response.code, 202);
assert_eq!(
successful_response.data,
"{\"status\":\"success\"}".as_bytes()
);
}
#[test]
fn delete() {
let server = Server::run();
let m = all_of![
request::method_path("DELETE", "/v1/identities"),
request::headers(contains(key("authorization"))),
];
server.expect(
Expectation::matching(m)
.respond_with(status_code(202).body("{\"status\":\"success\"}")),
);
let kp = KeyPair::new(KeyPairType::Ed25519);
let client = Rest::new("my-self-identity", kp);
let url = server.url_str("/v1/identities");
let response = client.delete(&url);
assert!(response.is_ok());
let successful_response = response.unwrap();
assert_eq!(successful_response.code, 202);
assert_eq!(
successful_response.data,
"{\"status\":\"success\"}".as_bytes()
);
}
}