use Request;
use std::error::Error;
use std::io::Read;
pub trait Transport {
type Stream: Read;
fn transmit(self, request: &Request<'_>) -> Result<Self::Stream, Box<dyn Error + Send + Sync>>;
}
#[cfg(feature = "http")]
pub mod http {
extern crate mime;
extern crate reqwest;
use self::mime::Mime;
use self::reqwest::blocking::RequestBuilder;
use self::reqwest::header::{CONTENT_LENGTH, CONTENT_TYPE, USER_AGENT};
use {Request, Transport};
use std::error::Error;
use std::str::FromStr;
pub fn build_headers(builder: RequestBuilder, body_len: u64) -> RequestBuilder {
builder
.header(USER_AGENT, "Rust xmlrpc")
.header(CONTENT_TYPE, "text/xml; charset=utf-8")
.header(CONTENT_LENGTH, body_len)
}
pub fn check_response(
response: &reqwest::blocking::Response,
) -> Result<(), Box<dyn Error + Send + Sync>> {
if response.status().is_client_error() || response.status().is_server_error() {
return Err(format!("server response indicates error: {}", response.status()).into());
}
if let Some(content) = response
.headers()
.get(CONTENT_TYPE)
.and_then(|value| value.to_str().ok())
.and_then(|value| Mime::from_str(value).ok())
{
match (content.type_(), content.subtype()) {
(mime::TEXT, mime::XML) => {}
(ty, sub) => {
return Err(
format!("expected Content-Type 'text/xml', got '{}/{}'", ty, sub).into(),
)
}
}
}
Ok(())
}
impl Transport for RequestBuilder {
type Stream = reqwest::blocking::Response;
fn transmit(
self,
request: &Request<'_>,
) -> Result<Self::Stream, Box<dyn Error + Send + Sync>> {
let mut body = Vec::new();
request.write_as_xml(&mut body).unwrap();
let response = build_headers(self, body.len() as u64).body(body).send()?;
check_response(&response)?;
Ok(response)
}
}
}