#![allow(dead_code)]
use url::Url;
use unicase::UniCase;
use case_insensitive_hashmap::CaseInsensitiveHashMap;
use std::str::from_utf8;
use std::io::{ErrorKind, Error};
use json::JsonValue;
use std::fmt;
use std::collections::HashMap;
use std::iter::Iterator;
use wcookie::SetCookie;
use std::ops::{Deref, DerefMut};
pub const CONTENT_TYPE: &str = "Content-Type";
pub const APPLICATION_JSON: &str = "application/json";
pub const ACCEPT: &str = "Accept";
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum HttpMethod {GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH}
impl fmt::Display for HttpMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}",
match self {
Self::GET => "GET",
Self::HEAD => "HEAD",
Self::POST => "POST",
Self::PUT=> "PUT",
Self::DELETE=> "DELETE",
Self::CONNECT => "CONNECT",
Self::OPTIONS => "OPTIONS",
Self::TRACE => "TRACE",
Self::PATCH=> "PATCH"
}
)
}
}
#[derive(Clone, PartialEq, Debug)]
enum MessageBody {
None,
Single(Vec<u8>),
MultiPart
}
impl MessageBody {
fn is_none(&self) -> bool {
matches!(*self, Self::None)
}
fn is_single(&self) -> bool {
matches!(*self, Self::Single(_))
}
fn is_multipart(&self) -> bool {
matches!(*self, Self::MultiPart)
}
}
pub struct HeaderMap {
map : CaseInsensitiveHashMap<String>
}
impl HeaderMap {
pub fn new() -> HeaderMap {
HeaderMap {
map: CaseInsensitiveHashMap::new()
}
}
pub fn insert<K,V>(&mut self, key: K, value: V) -> bool
where K: Into<String>,
V: Into<String> {
self.map.insert(key.into(),value.into()).is_some()
}
pub fn contains_key<K>(&self, key: K) -> bool
where
K: Into<String>
{
self.map.contains_key(key.into())
}
pub fn get(&self, key: &str) -> Option<&str> {
self.map.get(key).map(|s| s.as_str())
}
pub fn iter(&self) -> HeaderIter {
HeaderIter {
iter: self.map.iter()
}
}
}
impl From<Vec<(String, String)>> for HeaderMap {
fn from(value: Vec<(String, String)>) -> Self {
let mut owned = value;
let mut result = HeaderMap::new();
loop {
if let Some((k,v)) = owned.pop() {
result.insert(k,v);
} else {
break;
}
}
result
}
}
impl From<&Vec<(&str, &str)>> for HeaderMap {
fn from(value: &Vec<(&str, &str)>) -> Self {
let mut result = HeaderMap::new();
for (k, v) in value.iter() {
result.insert(*k,*v);
}
result
}
}
pub struct HeaderIter<'a> {
iter: std::collections::hash_map::Iter<'a, UniCase<String>, String>
}
impl<'a> Iterator for HeaderIter<'a> {
type Item = (&'a str, &'a str);
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(key, value)| (key.as_str(), value.as_str()) )
}
}
pub struct KeyValueMap {
map : HashMap<String, String>
}
impl KeyValueMap {
pub fn new() -> KeyValueMap {
KeyValueMap {
map: HashMap::new()
}
}
pub fn insert<K,V>(&mut self, key: K, value: V) -> bool
where K: Into<String>,
V: Into<String> {
self.map.insert(key.into(),value.into()).is_some()
}
pub fn get(&self, key: &str) -> Option<&str> {
self.map.get(key).map(|s| s.as_str())
}
pub fn contains_key(&self, key: &str) -> bool {
self.map.contains_key(key)
}
pub fn iter(&self) -> KeyValueIter {
KeyValueIter {
iter: self.map.iter()
}
}
}
pub struct KeyValueIter<'a> {
iter: std::collections::hash_map::Iter<'a, String, String>
}
impl<'a> Iterator for KeyValueIter<'a> {
type Item = (&'a str, &'a str);
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(key, value)| (key.as_str(), value.as_str()) )
}
}
pub struct HttpMessage {
headers: HeaderMap,
body: MessageBody
}
impl HttpMessage {
pub fn new() -> HttpMessage {
HttpMessage {
headers : HeaderMap::new(),
body: MessageBody::None
}
}
pub fn insert_header<K,V>(&mut self, key: K, value: V) -> &mut Self
where K: Into<String>,
V: Into<String> {
self.headers.insert(key, value);
self
}
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.headers
}
pub fn has_single_body(&self) -> bool {
self.body.is_single()
}
pub fn has_multipart_body(&self) -> bool {
self.body.is_multipart()
}
pub fn set_body(&mut self, data: Vec<u8>) -> &mut Self {
self.body = MessageBody::Single(data);
self
}
pub fn body (&self) -> Option<&Vec<u8>> {
if let MessageBody::Single(ref body) = self.body {
Some(body)
} else {
None
}
}
pub fn set_json(&mut self, data: &JsonValue) -> &mut Self {
let pretty = data.pretty(4);
self.headers.insert(CONTENT_TYPE, APPLICATION_JSON);
self.set_body(pretty.into_bytes())
}
pub fn json(&self) -> Result<JsonValue, Error> {
if ! self.body.is_single() {
return Err(Error::new(ErrorKind::InvalidData, "Empty body"));
}
let str_body = from_utf8(self.body().unwrap());
if str_body.is_err() {
return Err(Error::new(ErrorKind::InvalidData, str_body.err().unwrap()));
}
let result = json::parse(str_body.unwrap());
return if result.is_ok() {
Ok(result.unwrap())
} else {
Err(Error::new(ErrorKind::InvalidData, result.err().unwrap()))
}
}
}
pub struct Request {
base: HttpMessage,
method: HttpMethod,
target: String,
url: Option<Url>,
cookies: KeyValueMap,
params: KeyValueMap
}
impl Request {
pub fn new<S>(method: HttpMethod, url:S) -> Request
where S: Into<String>
{
let target = url.into();
let parsed_url = Url::parse(target.as_str());
Request {
base: HttpMessage::new(),
method,
target,
url: parsed_url.ok(),
cookies: KeyValueMap::new(),
params: KeyValueMap::new()
}
}
pub fn connect<S>(url: S) -> Request
where S: Into::<String>
{
Self::new(HttpMethod::CONNECT, url)
}
pub fn delete<S>(url: S) -> Request
where S: Into::<String>
{
Self::new(HttpMethod::DELETE, url)
}
pub fn get<S>(url: S) -> Request
where S: Into::<String>
{
Self::new(HttpMethod::GET, url)
}
pub fn head<S>(url: S) -> Request
where S: Into::<String>
{
Self::new(HttpMethod::HEAD, url)
}
pub fn options<S>(url: S) -> Request
where S: Into::<String>
{
Self::new(HttpMethod::OPTIONS, url)
}
pub fn patch<S>(url: S) -> Request
where S: Into::<String>
{
Self::new(HttpMethod::PATCH, url)
}
pub fn post<S>(url: S) -> Request
where S: Into::<String>
{
Self::new(HttpMethod::POST, url)
}
pub fn put<S>(url: S) -> Request
where S: Into::<String>
{
Self::new(HttpMethod::PUT, url)
}
pub fn trace<S>(url: S) -> Request
where S: Into::<String>
{
Self::new(HttpMethod::TRACE, url)
}
pub fn method(&self) -> HttpMethod {
self.method
}
pub fn insert_param<K, V>(&mut self, key: K, value: V) -> &mut Self
where K: Into<String>,
V: Into<String>
{
self.params.insert(key, value);
self
}
pub fn params(&self) -> &KeyValueMap {
&self.params
}
pub fn params_mut(&mut self) -> &mut KeyValueMap {
&mut self.params
}
pub fn insert_cookie<K, V>(&mut self, key: K, value: V) -> &mut Self
where K: Into<String>,
V: Into<String>
{
self.cookies.insert(key, value);
self
}
pub fn cookies(&self) -> &KeyValueMap {
&self.cookies
}
pub fn cookies_mut(&mut self) -> &mut KeyValueMap {
&mut self.params
}
pub fn target(&self) -> &str {
self.target.as_str()
}
pub fn url(&self) -> Option<&Url> {
self.url.as_ref()
}
}
impl Deref for Request {
type Target = HttpMessage;
fn deref(&self) -> &HttpMessage {
&self.base
}
}
impl DerefMut for Request {
fn deref_mut(&mut self) -> &mut HttpMessage {
&mut self.base
}
}
impl fmt::Display for Request {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{} {}", self.method, &self.target)?;
let headers = self.headers.iter();
for (key,value) in headers {
writeln!(f, "{}={}", key, value)?;
}
Ok(())
}
}
pub type HttpStatusCode = u16;
pub const HTTP_100_CONTINUE: u16 = 100;
pub const HTTP_101_SWITCHING_PROTOCOLS: u16 = 101;
pub const HTTP_200_OK: u16 = 200;
pub const HTTP_201_CREATED: u16 = 201;
pub const HTTP_202_ACCEPTED: u16 = 202;
pub const HTTP_203_NON_AUTHORIZATIVE_INFORMATION: u16 = 203;
pub const HTTP_204_NO_CONTENT: u16 = 204;
pub const HTTP_205_RESET_CONTENT: u16 = 205;
pub const HTTP_300_MULTIPLE_CHOICES: u16 = 300;
pub const HTTP_301_MOVED_PERMANENTLY: u16 = 301;
pub const HTTP_302_FOUND: u16 = 302;
pub const HTTP_303_SEE_OTHER: u16 = 303;
pub const HTTP_305_RESET_CONTENT: u16 = 305;
pub const HTTP_307_TEMPORARY_REDIRECT: u16 = 307;
pub const HTTP_400_BAD_REQUEST: u16 = 400;
pub const HTTP_401_UNAUTHORIZED: u16 = 401;
pub const HTTP_402_FORBIDDEN: u16 = 402;
pub const HTTP_404_NOT_FOUND: u16 = 404;
pub const HTTP_405_METHOD_NOT_ALLOWED: u16 = 405;
pub const HTTP_406_NOT_ACCEPTABLE: u16 = 406;
pub const HTTP_408_REQUEST_TIMEOUT: u16 = 408;
pub const HTTP_409_CONFLICT: u16 = 409;
pub const HTTP_410_GONE: u16 = 410;
pub const HTTP_411_LENGTH_REQURED: u16 = 411;
pub const HTTP_413_PAYLOAD_TOO_LARGE: u16 = 413;
pub const HTTP_414_URI_TOO_LONG: u16 = 414;
pub const HTTP_415_UNSUPORTED_MEDIA_TYPE: u16 = 415;
pub const HTTP_417_EXPECTATION_FAILED: u16 = 417;
pub const HTTP_426_UPGRADE_REQUIRED: u16 = 426;
pub const HTTP_500_INTERNAL_SERVE_ERROR: u16 = 500;
pub const HTTP_501_NOT_IMPLEMENTED: u16 = 501;
pub const HTTP_502_BAD_GATEWAY: u16 = 502;
pub const HTTP_503_SERVICE_UNAVAILABLE: u16 = 503;
pub const HTTP_504_GATEWAY_TIMEOUT: u16 = 504;
pub const HTTP_505_HTTP_VERSION_NOT_SUPPORTED: u16 = 505;
type SetCookies = Vec<SetCookie>;
pub struct Response {
base: HttpMessage,
status_code: HttpStatusCode,
cookies: SetCookies,
auth: Vec<String>,
proxy_auth: Vec<String>
}
impl Response {
pub fn new(status: HttpStatusCode) -> Response {
Response {
base: HttpMessage::new(),
cookies: SetCookies::new(),
status_code: status,
auth: Vec::new(),
proxy_auth: Vec::new()
}
}
pub fn status_code(&self) -> HttpStatusCode {
self.status_code
}
pub fn insert_cookie(&mut self, value: SetCookie) -> &mut Self {
self.cookies.push(value);
self
}
pub fn cookies(&self) -> Vec<&SetCookie> {
self.cookies.iter().collect()
}
pub fn insert_auth_headers<S: Into<String>>(&mut self, auth: S) -> &mut Self {
self.auth.push(auth.into());
self
}
pub fn auth_headers(&self) -> &Vec<String> {
&self.auth
}
pub fn auth_headers_mut(&mut self) -> &mut Vec<String> {
&mut self.auth
}
pub fn insert_proxy_auth_header<S: Into<String>>(&mut self, auth: S) -> &mut Self {
self.proxy_auth.push(auth.into());
self
}
pub fn proxy_auth_headers(&self) -> &Vec<String> {
&self.proxy_auth
}
pub fn proxy_auth_headers_mut(&mut self) -> &mut Vec<String> {
&mut self.proxy_auth
}
}
impl Deref for Response {
type Target = HttpMessage;
fn deref(&self) -> &HttpMessage {
&self.base
}
}
impl DerefMut for Response {
fn deref_mut(&mut self) -> &mut HttpMessage {
&mut self.base
}
}
#[cfg(test)]
mod test_request;
#[cfg(test)]
mod test_response;