Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@ name = "xml-rpc"
readme = "README.md"
repository = "https://github.com/adnanademovic/xml-rpc-rs"
version = "0.1.0"
edition = "2021"


[dependencies]
base64 = "0.22.1"
error-chain = "0.12.4"
hyper = "0.10.15"
hyper = "1.6.0"
reqwest = "0.12.12"
lazy_static = "1.5.0"
regex = "1.11.1"
serde = { version = "1.0.217", features = ["derive"] }
serde-xml-rs = "0.6.0"
serde_bytes = "0.11.15"
xml-rs = "0.8.25"
rouille = "3.6.2"
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread"] }

[dev-dependencies]
tokio-test = "0.4.4"
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# XML-RPC

A pure Rust implementation of [xml-rpc](http://xmlrpc.scripting.com/spec.html).

## Getting Started

For examples check src/bin.
93 changes: 93 additions & 0 deletions src/async_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//! Asynchronous XML-RPC client
//!
//! ## Example Usage:
//!
//! ```rust
//! # tokio_test::block_on(async {
//! use xml_rpc::async_client::Client;
//! let mut client = Client::new().unwrap();
//! let uri = "http://localhost:8080/".parse().unwrap();
//! let res: Result<Result<String, _>, _> = client.call(&uri, "echo", "my param").await;
//! # })
//! ```
use super::error::{Result, ResultExt};
use super::xmlfmt::{from_params, into_params, parse, Call, Fault, Params, Response};
use reqwest::header::CONTENT_TYPE;
use reqwest::{Client as HyperClient, Url};
use serde::{Deserialize, Serialize};
use std;
use std::io::Cursor;

pub async fn call_value<Tkey>(uri: &Url, name: Tkey, params: Params) -> Result<Response>
where
Tkey: Into<String>,
{
Client::new()?.call_value(uri, name, params).await
}

pub async fn call<'a, Tkey, Treq, Tres>(
uri: &Url,
name: Tkey,
req: Treq,
) -> Result<std::result::Result<Tres, Fault>>
where
Tkey: Into<String>,
Treq: Serialize,
Tres: Deserialize<'a> + Sync + Send,
{
Client::new()?.call(uri, name, req).await
}

pub struct Client {
client: HyperClient,
}

impl Client {
pub fn new() -> Result<Client> {
let client = HyperClient::new();
Ok(Client { client })
}

pub async fn call_value<Tkey>(&self, uri: &Url, name: Tkey, params: Params) -> Result<Response>
where
Tkey: Into<String>,
{
use super::xmlfmt::value::ToXml;
let body_str = Call {
name: name.into(),
params,
}
.to_xml();
let response = self
.client
.post(uri.as_ref())
.header(CONTENT_TYPE, "xml")
.body(body_str)
.send()
.await
.chain_err(|| "Failed to run the HTTP request within hyper.")?;
let body = response
.bytes()
.await
.chain_err(|| "Failed to get response bytes")?;
parse::response(Cursor::new(body)).map_err(Into::into)
}

pub async fn call<'a, Tkey, Treq, Tres>(
&self,
uri: &Url,
name: Tkey,
req: Treq,
) -> Result<std::result::Result<Tres, Fault>>
where
Tkey: Into<String>,
Treq: Serialize,
Tres: Deserialize<'a>,
{
match self.call_value(uri, name, into_params(&req)?).await {
Ok(Ok(v)) => from_params(v).map(Ok).map_err(Into::into),
Ok(Err(v)) => Ok(Err(v)),
Err(v) => Err(v),
}
}
}
52 changes: 17 additions & 35 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use super::error::{Result, ResultExt};
use super::xmlfmt::{from_params, into_params, parse, Call, Fault, Params, Response};
use hyper::{self, Client as HyperClient};
use crate::async_client;

use super::error::Result;
use super::xmlfmt::{Fault, Params, Response};
use reqwest::Url;
use serde::{Deserialize, Serialize};
use std;
use Url;

use hyper::header::Headers;
header! { (ContentType, "ContentType") => [String] }

pub fn call_value<Tkey>(uri: &Url, name: Tkey, params: Params) -> Result<Response>
where
Expand All @@ -29,40 +27,24 @@ where
}

pub struct Client {
client: HyperClient,
client: async_client::Client,
}

impl Client {
pub fn new() -> Result<Client> {
let client = HyperClient::new();
let client = async_client::Client::new()?;
Ok(Client { client })
}

pub fn call_value<Tkey>(&mut self, uri: &Url, name: Tkey, params: Params) -> Result<Response>
where
Tkey: Into<String>,
{
use super::xmlfmt::value::ToXml;
let body_str = Call {
name: name.into(),
params,
}
.to_xml();
let bytes: &[u8] = body_str.as_bytes();
let body = hyper::client::Body::BufBody(bytes, bytes.len());

let mut headers = Headers::new();
headers.set(ContentType("xml".to_owned()));

let response = self
.client
.post(uri.as_ref())
.headers(headers)
.body(body)
.send()
.chain_err(|| "Failed to run the HTTP request within hyper.")?;

parse::response(response).map_err(Into::into)
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async { self.client.call_value(uri, name, params).await })
}

pub fn call<'a, Tkey, Treq, Tres>(
Expand All @@ -76,10 +58,10 @@ impl Client {
Treq: Serialize,
Tres: Deserialize<'a>,
{
match self.call_value(uri, name, into_params(&req)?) {
Ok(Ok(v)) => from_params(v).map(Ok).map_err(Into::into),
Ok(Err(v)) => Ok(Err(v)),
Err(v) => Err(v),
}
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async { self.client.call(uri, name, req).await })
}
}
3 changes: 1 addition & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
extern crate base64;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate hyper;
#[macro_use]
extern crate lazy_static;
Expand All @@ -16,12 +15,12 @@ extern crate serde_bytes;
extern crate serde_xml_rs;
extern crate xml;

pub mod async_client;
pub mod client;
pub mod error;
pub mod server;
mod xmlfmt;

pub use client::{call, call_value, Client};
pub use hyper::Url;
pub use server::Server;
pub use xmlfmt::{from_params, into_params, Call, Fault, Params, Response, Value};