use json::{JsonValue, object};
use std::{fs, str};
use std::collections::HashMap;
use std::fmt::Debug;
use std::str::FromStr;
use log::debug;
use reqwest::{Method as OtherMethod};
use reqwest::blocking::multipart::{Form, Part};
use serde_json::Value;
#[derive(Debug)]
pub struct Http {
pub url: String,
method: Method,
header: HashMap<String, String>,
debug: bool,
read_timeout: u64,
write_timeout: u64,
pub query: String,
content_type: ContentType,
param_form_data: Option<Form>,
param_form_urlencoded: HashMap<String, String>,
param_raw_json: Value,
param_raw_text: String,
param_raw_stream: Vec<u8>,
status: u16,
response_content_type: ContentType,
response_header: HashMap<String, String>,
}
impl Default for Http {
fn default() -> Self {
Self::new()
}
}
impl Http {
pub fn new() -> Http {
Self {
url: "".to_string(),
method: Method::new(),
header: Default::default(),
debug: false,
read_timeout: 5,
write_timeout: 5,
query: "".to_string(),
content_type: ContentType::None,
param_form_data: None,
param_form_urlencoded: Default::default(),
param_raw_json: Default::default(),
param_raw_text: "".to_string(),
param_raw_stream: vec![],
status: 0,
response_content_type: ContentType::None,
response_header: Default::default(),
}
}
pub fn set_read_timeout(&mut self, secs: u64) -> &mut Self {
self.read_timeout = secs;
self
}
pub fn set_write_timeout(&mut self, secs: u64) -> &mut Self {
self.write_timeout = secs;
self
}
pub fn debug(&mut self, open: bool) -> &mut Self {
self.debug = open;
self
}
pub fn get(&mut self, url: &str) -> &mut Self {
self.url = url.to_string();
self.method = Method::GET;
self
}
pub fn post(&mut self, url: &str) -> &mut Http {
self.url = url.to_string();
self.method = Method::POST;
self
}
pub fn options(&mut self, url: &str) -> &mut Http {
self.url = url.to_string();
self.method = Method::OPTIONS;
self
}
pub fn put(&mut self, url: &str) -> &mut Http {
self.url = url.to_string();
self.method = Method::PUT;
self
}
pub fn patch(&mut self, url: &str) -> &mut Http {
self.url = url.to_string();
self.method = Method::PATCH;
self
}
pub fn head(&mut self, url: &str) -> &mut Http {
self.url = url.to_string();
self.method = Method::HEAD;
self
}
pub fn delete(&mut self, url: &str) -> &mut Http {
self.url = url.to_string();
self.method = Method::DELETE;
self
}
pub fn header(&mut self, key: &str, value: &str) -> &mut Http {
self.header.insert(key.to_string(), value.to_string());
self
}
pub fn bearer_auth(&mut self, token: &str) -> &mut Http {
self.header.insert("Authorization".to_string(), format!("Bearer {}", token));
self
}
pub fn cookie(&mut self, key: &str, value: &str) -> &mut Http {
self.header.insert("Cookie".to_string(), format!("{}={}", key, value));
self
}
pub fn query(&mut self, mut data: JsonValue) -> &mut Self {
self.content_type = ContentType::Plain;
let mut t = vec![];
for (key, value) in data.entries_mut() {
t.push(format!("{}={}", key, value))
};
self.query = t.join("&");
if !self.query.is_empty() && !self.url.contains("?") {
self.url = format!("{}?{}", self.url, self.query);
}
self
}
pub fn form_data(&mut self, data: Vec<FormData>) -> &mut Self
{
self.content_type = ContentType::FormData;
let mut form = Form::new();
for item in data {
match item {
FormData::Text(key, value, mime) => {
let r = if !mime.is_empty() {
Part::text(value.to_string()).mime_str(mime.clone().as_str())
} else {
Ok(Part::text(value.to_string()))
};
form = form.part(key, r.unwrap());
}
FormData::File(key, path, mime) => {
let r = if !mime.is_empty() {
Part::bytes(fs::read(path).unwrap()).file_name(key.to_string()).mime_str(mime.clone().as_str())
} else {
Ok(Part::bytes(fs::read(path).unwrap()).file_name(key.to_string()))
};
form = form.part(key, r.unwrap());
}
FormData::None => {}
}
}
self.param_form_data = Some(form);
self
}
pub fn form_urlencoded(&mut self, data: JsonValue) -> &mut Self {
self.content_type = ContentType::FormUrlencoded;
let mut params = HashMap::new();
for (key, value) in data.entries() {
params.insert(key.to_string(), value.to_string());
}
self.param_form_urlencoded = params;
self
}
pub fn raw_json(&mut self, data: JsonValue) -> &mut Self {
self.content_type = ContentType::Json;
if !data.is_empty() {
self.param_raw_json = Value::from_str(&data.dump()).unwrap();
}
self
}
pub fn raw_javascript(&mut self, data: &str) -> &mut Self {
self.header("Content-Type", ContentType::Javascript.str().as_str());
self.content_type = ContentType::Javascript;
self.param_raw_text = data.to_string();
self
}
pub fn raw_xml(&mut self, data: &str) -> &mut Self {
self.header("Content-Type", ContentType::Xml.str().as_str());
self.content_type = ContentType::Xml;
self.param_raw_text = data.to_string();
self
}
pub fn raw_html(&mut self, data: &str) -> &mut Self {
self.header("Content-Type", ContentType::Html.str().as_str());
self.content_type = ContentType::Html;
self.param_raw_text = data.to_string();
self
}
pub fn raw_text(&mut self, data: &str) -> &mut Self {
self.header("Content-Type", ContentType::Plain.str().as_str());
self.content_type = ContentType::Plain;
self.param_raw_text = data.to_string();
self
}
pub fn raw_stream(&mut self, data: Vec<u8>) -> &mut Self {
self.header("Content-Type", ContentType::Stream.str().as_str());
self.content_type = ContentType::Stream;
self.header("Content-Length", data.len().to_string().leak());
self.param_raw_stream = data;
self
}
pub fn raw_binary(&mut self, data: Vec<u8>, content_type: &str) -> &mut Self {
self.header("Content-Type", content_type);
self.content_type = ContentType::Stream;
self.header("Content-Length", data.len().to_string().leak());
self.param_raw_stream = data;
self
}
fn send(&mut self, mode: &str) -> Result<Vec<u8>, String> {
let client = reqwest::blocking::Client::new();
let mut request = match self.method {
Method::GET => {
client.get(self.url.as_str())
}
Method::POST => {
client.post(self.url.as_str())
}
Method::OPTIONS => {
client.request(OtherMethod::OPTIONS, self.url.as_str())
}
Method::PATCH => {
client.patch(self.url.as_str())
}
Method::HEAD => {
client.head(self.url.as_str())
}
Method::DELETE => {
client.delete(self.url.as_str())
}
Method::PUT => {
client.put(self.url.as_str())
}
Method::TRACE => {
client.request(OtherMethod::TRACE, self.url.as_str())
}
Method::NONE => {
client.request(OtherMethod::CONNECT, self.url.as_str())
}
};
match self.content_type {
ContentType::FormData => {
request = request.multipart(self.param_form_data.take().unwrap());
}
ContentType::FormUrlencoded => {
request = request.form(&self.param_form_urlencoded);
}
ContentType::Json => {
request = request.json(&self.param_raw_json);
}
ContentType::Xml | ContentType::Plain | ContentType::Html | ContentType::Javascript => {
let body = self.param_raw_text.as_mut_str();
request = request.body(body.as_bytes().to_vec());
}
ContentType::Stream => {
request = request.body(self.param_raw_stream.clone());
}
ContentType::None => {
let body = self.param_raw_text.as_mut_str();
request = request.body(body.as_bytes().to_vec());
}
}
for (key, value) in self.header.iter() {
request = request.header(key.clone(), value.clone());
}
if self.debug {
debug!("发送数据: {:#?}",request);
}
match request.send() {
Ok(e) => {
if self.debug {
debug!("返回数据: {:#?}",e);
}
self.status = e.status().as_u16();
self.response_header = HashMap::new();
for (key, value) in e.headers().clone() {
if key.clone().is_some() {
self.response_header.insert(key.unwrap().to_string(), value.to_str().unwrap().trim_start_matches("\"").trim_end_matches("\"").to_string());
}
}
match self.response_header.get("content-type") {
Some(header) => {
let value = header.clone();
self.response_content_type = ContentType::from(&value);
match value {
s if s.contains("application/json") => {
let json = e.json::<Value>();
match json {
Ok(e) => {
Ok(e.to_string().as_bytes().to_vec())
}
Err(e) => Err(e.to_string())
}
}
_ => {
let text = e.text();
match text {
Ok(e) => {
Ok(e.as_bytes().to_vec())
}
Err(e) => Err(e.to_string())
}
}
}
}
None => {
match mode {
"json" => {
let r = e.json::<Value>();
match r {
Ok(e) => Ok(e.to_string().as_bytes().to_vec()),
Err(_e) => Ok("".to_string().as_bytes().to_vec())
}
}
"text" => {
let r = e.text();
match r {
Ok(e) => Ok(e.as_bytes().to_vec()),
Err(e) => Err(e.to_string())
}
}
"cookie" => {
let cookies = e.cookies().collect::<Vec<_>>();
let mut list = HashMap::new();
for cookie in cookies.iter() {
list.insert(cookie.name().to_string(), cookie.value().to_string());
}
Ok(JsonValue::from(list).dump().as_bytes().to_vec())
}
"stream" => {
let r = e.bytes().unwrap().to_vec();
Ok(r)
}
"info" => {
let r = e.text();
match r {
Ok(e) => Ok(e.as_bytes().to_vec()),
Err(e) => Err(e.to_string())
}
}
&_ => {
Ok(vec![])
}
}
}
}
}
Err(e) => {
Err(e.to_string())
}
}
}
pub fn cookies(&mut self) -> Result<JsonValue, String> {
match self.send("cookie") {
Ok(e) => {
let res = unsafe { String::from_utf8_unchecked(e) };
Ok(json::parse(&res).unwrap_or(object! {}))
}
Err(e) => Err(e)
}
}
pub fn text(&mut self) -> Result<String, String> {
match self.send("text") {
Ok(e) => {
let res = unsafe { String::from_utf8_unchecked(e) };
Ok(res)
}
Err(e) => Err(e)
}
}
pub fn json(&mut self) -> Result<JsonValue, String> {
match self.send("json") {
Ok(e) => {
let res = unsafe { String::from_utf8_unchecked(e) };
Ok(json::parse(&res).unwrap_or(object! {}))
}
Err(e) => Err(e)
}
}
pub fn info(&mut self) -> Result<(u16, String, String, HashMap<String, String>), String> {
match self.send("info") {
Ok(e) => {
let res = unsafe { String::from_utf8_unchecked(e) };
Ok((self.status, self.response_content_type.str(), res, self.response_header.clone()))
}
Err(e) => Err(e)
}
}
pub fn stream(&mut self) -> Result<Vec<u8>, String> {
match self.send("stream") {
Ok(e) => Ok(e),
Err(e) => Err(e)
}
}
}
#[derive(Clone, Debug)]
pub enum Method {
GET,
POST,
OPTIONS,
PATCH,
HEAD,
DELETE,
TRACE,
PUT,
NONE,
}
impl Method {
fn new() -> Self {
Method::GET
}
pub fn to_str(&self) -> String {
match self {
Method::GET => "GET",
Method::POST => "POST",
Method::OPTIONS => "OPTIONS",
Method::PATCH => "PATCH",
Method::HEAD => "HEAD",
Method::DELETE => "DELETE",
Method::TRACE => "TRACE",
Method::PUT => "PUT",
Method::NONE => "NONE"
}.to_string()
}
}
#[derive(Clone, Debug)]
pub enum Version {
Http09,
Http10,
Http11,
H2,
H3,
None,
}
impl Version {
pub fn str(&mut self) -> String {
match self {
Version::Http09 => "HTTP/0.9",
Version::Http10 => "HTTP/1.0",
Version::Http11 => "HTTP/1.1",
Version::H2 => "HTTP/2.0",
Version::H3 => "HTTP/3.0",
Version::None => ""
}.to_string()
}
pub fn from(name: &str) -> Version {
match name {
"HTTP/0.9" => Self::Http09,
"HTTP/1.0" => Self::Http10,
"HTTP/1.1" => Self::Http11,
"HTTP/2.0" => Self::H2,
"HTTP/3.0" => Self::H3,
_ => Self::None
}
}
pub fn set_version(name: &str) -> Version {
match name {
"0.9" => Self::Http09,
"1.0" => Self::Http10,
"1.1" => Self::Http11,
"2.0" => Self::H2,
"3.0" => Self::H3,
_ => Self::None
}
}
}
#[derive(Clone, Debug)]
pub enum Body {
Json(JsonValue),
Text(String),
Xml(String),
FormData(Vec<u8>),
XWwwFormUrlencoded(String),
None,
}
impl Default for Body {
fn default() -> Self {
Self::new()
}
}
impl Body {
pub fn new() -> Body {
Self::None
}
pub fn bytes(&mut self) -> Vec<u8> {
match self {
Body::Json(e) => e.to_string().as_bytes().to_vec(),
Body::Text(e) => e.clone().as_bytes().to_vec(),
Body::Xml(e) => e.clone().as_bytes().to_vec(),
Body::FormData(e) => e.to_vec().clone(),
Body::XWwwFormUrlencoded(e) => e.clone().as_bytes().to_vec(),
Body::None => "".as_bytes().to_vec()
}
}
}
#[derive(Clone, Debug)]
pub enum ContentType {
FormData,
FormUrlencoded,
Json,
Xml,
Plain,
Html,
Stream,
Javascript,
None,
}
impl ContentType {
pub fn str(&mut self) -> String {
match self {
ContentType::Plain => "text/plain",
ContentType::Html => "text/html",
ContentType::Stream => "application/octet-stream",
ContentType::Javascript => "application/javascript",
ContentType::FormData => "multipart/form-data",
ContentType::Json => "application/json",
ContentType::Xml => "application/xml",
ContentType::None => "",
ContentType::FormUrlencoded => "application/x-www-form-urlencoded"
}.to_string()
}
pub fn from(name: &str) -> Self {
match name {
"application/xml" => ContentType::Xml,
"text/plain" => ContentType::Plain,
"text/html" => ContentType::Html,
"application/octet-stream" => ContentType::Stream,
"application/javascript" => ContentType::Javascript,
"multipart/form-data" => ContentType::FormData,
"application/json" => ContentType::Json,
"application/x-www-form-urlencoded" => ContentType::FormUrlencoded,
_ => ContentType::None
}
}
}
#[derive(Clone, Debug)]
pub enum FormData {
Text(String, JsonValue, String),
File(String, String, String),
None,
}