diff --git a/CHANGELOG.md b/CHANGELOG.md index 33ef79ee..817f81dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ ## Unreleased -No changes. +* Updated to Rust 2021 Syntax +* Updated `reqwest`to 0.11.10 +* Added `tokio` dependency for async runtime (feature rt-multi-thread) + +- Refactored to not use `reqwest::blocking`, all interfaces stay stable ## 0.15.1 - 2021-11-02 diff --git a/Cargo.toml b/Cargo.toml index 838ce593..75b37c2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,8 @@ categories = ["network-programming", "encoding"] readme = "README.md" license = "CC0-1.0" name = "xmlrpc" -version = "0.15.1" +version = "0.16.0" +edition = "2021" # cargo-release configuration [package.metadata.release] @@ -40,12 +41,21 @@ maintenance = { status = "actively-developed" } [dependencies] # public -iso8601 = "0.4.0" -reqwest = { version = "0.11.0", features = [ "blocking" ], default-features = false, optional = true } +iso8601 = "0.5.0" # private mime = { version = "0.3", optional = true } base64 = "0.13.0" -xml-rs = "0.8.0" +xml-rs = "0.8.4" +futures = "0.3.21" + +[dependencies.reqwest] +version = "0.11.10" +default-features = false +optional = true + +[dependencies.tokio] +version = "1.17.0" +features = ["rt-multi-thread"] [dev-dependencies] version-sync = "0.9" diff --git a/README.md b/README.md index d831ace6..da9e174b 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Start by adding an entry to your `Cargo.toml`: ```toml [dependencies] -xmlrpc = "0.15.1" +xmlrpc = "0.16.0" ``` Then import the crate into your Rust code: diff --git a/examples/custom-header.rs b/examples/custom-header.rs index bc310390..ed943629 100644 --- a/examples/custom-header.rs +++ b/examples/custom-header.rs @@ -3,19 +3,21 @@ extern crate reqwest; extern crate xmlrpc; +use futures::executor::block_on; use xmlrpc::http::{build_headers, check_response}; use xmlrpc::{Request, Transport}; -use reqwest::blocking::{Client, RequestBuilder, Response}; +use reqwest::{Client, RequestBuilder}; use reqwest::header::COOKIE; use std::error::Error; +use std::io::Cursor; /// Custom transport that adds a cookie header. struct MyTransport(RequestBuilder); impl Transport for MyTransport { - type Stream = Response; + type Stream = Cursor; fn transmit(self, request: &Request) -> Result> { let mut body = Vec::new(); @@ -23,14 +25,19 @@ impl Transport for MyTransport { .write_as_xml(&mut body) .expect("could not write request to buffer (this should never happen)"); - let response = build_headers(self.0, body.len() as u64) + let response = async move {build_headers(self.0, body.len() as u64) .header(COOKIE, "SESSION=123abc") // Our custom header will be a `Cookie` header .body(body) - .send()?; + .send().await.unwrap()}; + + let resp = block_on(response); - check_response(&response)?; + check_response(&resp)?; - Ok(response) + let rs = async move {resp.text().await.unwrap()}; + let rv = Cursor::new(block_on(rs)); + + Ok(rv) } } diff --git a/src/error.rs b/src/error.rs index 925412bc..458bf4c2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,6 @@ //! Defines error types used by this library. -use Value; +use crate::value::Value; use xml::common::TextPosition; use xml::reader::Error as XmlError; diff --git a/src/lib.rs b/src/lib.rs index 7dd870f2..8d974e25 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,9 +4,8 @@ //! //! [XML-RPC specification]: http://xmlrpc.scripting.com/spec.html -#![doc(html_root_url = "https://docs.rs/xmlrpc/0.15.1")] +#![doc(html_root_url = "https://docs.rs/xmlrpc/0.16.0")] #![warn(missing_debug_implementations)] -#![warn(rust_2018_idioms)] #![warn(missing_docs)] extern crate base64; diff --git a/src/parser.rs b/src/parser.rs index 48fd1f68..a14caac6 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,7 +1,7 @@ //! XML-RPC response parser. -use error::ParseError; -use {Fault, Value}; +use crate::error::{ParseError, Fault}; +use crate::value::Value; use base64; use iso8601::datetime; @@ -353,7 +353,7 @@ pub fn parse_response(reader: &mut R) -> ParseResult { mod tests { use super::*; - use error::Fault; + use crate::error::Fault; use Value; use std::fmt::Debug; diff --git a/src/request.rs b/src/request.rs index e59246de..60c8dd5c 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,11 +1,11 @@ #[cfg(feature = "http")] extern crate reqwest; -use error::{Error, RequestErrorKind}; -use parser::parse_response; -use transport::Transport; -use utils::escape_xml; -use Value; +use crate::error::{Error, RequestErrorKind}; +use crate::parser::parse_response; +use crate::transport::Transport; +use crate::utils::escape_xml; +use crate::value::Value; use std::collections::BTreeMap; use std::io::{self, Write}; @@ -81,6 +81,8 @@ impl<'a> Request<'a> { .transmit(self) .map_err(RequestErrorKind::TransportError)?; + //let mut reader = tr.as_bytes(); + let response = parse_response(&mut reader).map_err(RequestErrorKind::ParseError)?; let value = response.map_err(RequestErrorKind::Fault)?; @@ -111,7 +113,7 @@ impl<'a> Request<'a> { // While we could implement `Transport` for `T: IntoUrl`, such an impl might not be // completely obvious (as it applies to `&str`), so I've added this method instead. // Might want to reconsider if someone has an objection. - self.call(reqwest::blocking::Client::new().post(url)) + self.call(reqwest::Client::new().post(url)) } /// Formats this `Request` as a UTF-8 encoded XML document. diff --git a/src/transport.rs b/src/transport.rs index 4b76d1e4..e1f66842 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -1,5 +1,4 @@ -use Request; - +use crate::request::Request; use std::error::Error; use std::io::Read; @@ -35,7 +34,7 @@ pub trait Transport { /// return an appropriate [`Error`] to the caller. /// /// [`Error`]: struct.Error.html - fn transmit(self, request: &Request<'_>) -> Result>; + fn transmit(self, request: &Request<'_>) -> std::result::Result>; } // FIXME: Link to `Transport` and `RequestBuilder` using intra-rustdoc links. Relative links break @@ -65,10 +64,13 @@ pub mod http { extern crate mime; extern crate reqwest; + use std::io::Cursor; use self::mime::Mime; - use self::reqwest::blocking::RequestBuilder; + use self::reqwest::RequestBuilder; use self::reqwest::header::{CONTENT_LENGTH, CONTENT_TYPE, USER_AGENT}; - use {Request, Transport}; + use crate::request::Request; + use crate::transport::Transport; + use tokio::runtime::Runtime; use std::error::Error; use std::str::FromStr; @@ -95,7 +97,7 @@ pub mod http { /// Checks that a reqwest `Response` has a status code indicating success and verifies certain /// headers. pub fn check_response( - response: &reqwest::blocking::Response, + response: &reqwest::Response, ) -> Result<(), Box> { // This is essentially an open-coded version of `Response::error_for_status` that does not // consume the response. @@ -133,23 +135,37 @@ pub mod http { /// The request will be sent as specified in the XML-RPC specification: A default `User-Agent` /// will be set, along with the correct `Content-Type` and `Content-Length`. impl Transport for RequestBuilder { - type Stream = reqwest::blocking::Response; + //Chose Cursor as Cursor implements the Read Trait and has ownership + type Stream = Cursor; fn transmit( self, request: &Request<'_>, - ) -> Result> { + ) -> Result> { // First, build the body XML let mut body = Vec::new(); // This unwrap never panics as we are using `Vec` as a `Write` implementor, // and not doing anything else that could return an `Err` in `write_as_xml()`. request.write_as_xml(&mut body).unwrap(); + + // async part needs to go to separate thread because of interference with caller + let async_transport = async move { + let rv = build_headers(self, body.len() as u64).body(body).send().await?; + check_response(&rv).expect("No valid response"); + rv.text().await + }; + + // execute the async transport in an own thread, to the blocking async execution can be used + let rs = std::thread::spawn( || {Runtime::new().unwrap().block_on(async_transport)}).join().expect("Expected result from async thread"); + + // error handling of the return value + match rs { + Ok(o) => Ok(Cursor::new(o)), + Err(err) => Err(Box::new(err) as Box), + } - let response = build_headers(self, body.len() as u64).body(body).send()?; - - check_response(&response)?; + //let rs = std::thread::spawn( || {Runtime::new().unwrap().block_on(async_transport)}).join().unwrap().map_err(|error| Box::new(error) as Box); - Ok(response) } } -} +} \ No newline at end of file diff --git a/src/value.rs b/src/value.rs index fa556eb4..b7029d61 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,6 +1,6 @@ //! Contains the different types of values understood by XML-RPC. -use utils::{escape_xml, format_datetime}; +use crate::utils::{escape_xml, format_datetime}; use base64::encode; use iso8601::DateTime; diff --git a/tests/python.rs b/tests/python.rs index 089d7582..d1207350 100644 --- a/tests/python.rs +++ b/tests/python.rs @@ -96,6 +96,7 @@ fn run_tests() { Fault::from_value(&results[2]).expect("expected fault as third result"); } + fn main() { let mut reaper = match setup() { Ok(reap) => reap,