Giter VIP home page Giter VIP logo

xml-rpc-rs's Introduction

XML-RPC for Rust

crates.io docs.rs CI

This crate provides a simple implementation of the XML-RPC specification in stable Rust using xml-rs and reqwest.

Please refer to the changelog to see what changed in the last releases.

Rust support

This crate uses the same Rust versioning policy as tokio: It supports the last 3 stable Rust releases. Increasing the minimum supported version is not considered a breaking change as long as the latest 3 versions are still supported.

Usage

Start by adding an entry to your Cargo.toml:

[dependencies]
xmlrpc = "0.15.1"

Then import the crate into your Rust code:

extern crate xmlrpc;

See examples/client.rs for a small example which connects to a running Python XML-RPC server and calls a method. A more elaborate example that demonstrates how to implement a custom Transport to set a cookie header is provided in examples/custom-header.rs.

xml-rpc-rs's People

Contributors

andy128k avatar apollo13 avatar birkenfeld avatar bitdrip avatar blaenk avatar bwbuchanan avatar dependabot-preview[bot] avatar dependabot-support avatar faulesocke avatar internetionals avatar jonas-schievink avatar leoschwarz avatar mhordecki avatar ssnover avatar wgahnagl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

xml-rpc-rs's Issues

Expose chrono's DateTime instead of iso8601's

Chrono should provide more flexibility, while the iso8601 crate only has the bare minimum needed for parsing dates.

One thing to watch out for is that chrono apparently uses RFC 3339:

Why isn't this named parse_from_iso8601? That's because ISO 8601 allows some freedom over the syntax and RFC 3339 exercises that freedom to rigidly define a fixed format.

Need to make sure this is fully backward-compatible to the ISO 8601 format. Not allowing some freedom makes it sound like it's not.

Chrono is well-established as the standard date/time handling library in Rust's ecosystem, but XML-RPC uses the ISO 8601 format, which is more permissive than RFC 3339 which is used by chrono.

The best solution is probably to write conversion code from iso8601::DateTime to chrono::DateTime<FixedOffset> and put the latter in our Value enum.

New release with updated dependencies

There are some pull requests open for purely dependency updates. It would be really helpful if those could be merged and new release could be published (eg. 0.14.1).

Especially:

But the bump for #58 would also be nice as it reduces the cargo outdated reporting. I understand the sentiment in making it an internal only dependency, but a simple bump for now couldn't hurt.

Move to `failure`

This will probably wait until failure gets more stable (I think some breaking changes are planned) - unless someone wants it really badly, so feel free to open a PR.

Allow specifying XML version or default to XML v1.0

I know that XML v1.1 has been there quite some while now, but in the original XML-RPC Specification only XML v1.0 was used in the examples. Actually the specification doesn't explicitely mention which XML version to support but back at that time there was no XML version 1.0.

The spec is thus not very clear, however I actually tried using this crate to write authentication code for OpenSim and had some issues (and struggled figuring out what the issue was) until I realized that the server quietly rejects any kind of XML v1.1 request.

The two options I see are:

  • Defaulting to XML v1.0
  • Allowing the version to be specified explicitely (and have it default to one or the other version by default).

Any opinion on this?

Edit: I just realized that it's only in write_as_xml where version 1.0 is mentioned, so it might also just have been unintentional.

Investigate a Zero-Copy interface

Currently, this crate forces the use of many allocations.

It should be possible to make Value use Cow internally, as well as making Request store an &'a [Value] for the arguments (note that the name of the called method is already a &'a str). This makes adding single arguments impossible, but that's not really a problem since the user can just use a Vec themselves.

One thing to note is that this will probably make Value a bit clunkier to use: Cow is hard to match on. Also, I've noticed that implementing this causes a cyclic trait dependency.

Cookies

In order to implement some specific XML-RPC API, I need to access response cookies and also set request cookies. What is the best way to achieve this with the current code?

cookie support?

I'm trying to use an xml-rpc API that uses cookies for authentication (send auth command -> receive cookie -> send cookie with following commands). As far as I can see,
hyper::Client does not directly support storing cookies and I see no easy way to work around your library here since I can not access the Request and Response directly.

Would handling session cookies be a feature you'd want in your lib? I'd try to submit a pull request then.

Error when content-type is incorrect

Hey!
I've hit this error when receiving improperly formatted headers from remote servers. I know this is definitely supposed to work as written, and it is the server's fault for being improperly formatted, but it still makes it impossible to use this library's responses, despite the actual body of the response being valid xml.

Do you think there would be any value in creating a way to deliberately override or skip this function's content-type check?

format!("expected Content-Type 'text/xml', got '{}/{}'", ty, sub).into(),

How to create call?

Hi,
How can I set a xmlrpc call such as zend does?

$client = new Zend_XmlRpc_Client('https://domain.com/test/xmlrpc');

//auth data
$auth_data = array("user_name"=>'Firstname Lastname', "user_pass"=>"c10d791cd4821259dd4ec236243f7ef2");

$start_date = '2010-01-01';
$end_date = '2010-01-31';
//group id
$group_id = '3213';

$result = $client->call('billingEventsGet', array($auth_data, $start_date, $end_date, $group_id));
print_r($result);

How to do with rust?

Serde integration?

It should be possible, at least in some cases, to automatically (de-)serialize data from/to the XMLRPC format. This would be pretty cool to have. Investigate and prototype!

Write docs for the main module

I don't really like crates that just throw a list of types, functions and modules at you. The main module of the crate should contain at least a few paragraphs explaining what the crate does, how to use it (possibly by linking to the example), and a link to the spec it's based on.

How to use?

Hi,
Could you give me some example how to make requests?

extern crate xmlrpc;

use xmlrpc::Request;

fn main() {
    let custlist_req = Request::new("custList");

    let custlist_result = custlist_req.call_url("https://domailn.com/sms/xmlrpc");

    println!("Result: {:?}", custlist_result);

}

But I'm getting error :/

Result: Err(Error(TransportError("expected Content-Type \'text/xml\', got \'text/html\'")))

As arguments I also have send something like this:

<?xml version="1.0"?>
<methodCall>
    <methodName>custList</methodName>
    <params>
        <param>
            <value>
                <struct>
                    <member>
                        <name>user_name</name>
                        <value>
                            <string>User Name</string>
                        </value>
                    </member>
                    <member>
                        <name>user_pass</name>
                        <value>
                            <string>PasswordString</string>
                        </value>
                    </member>
                </struct>
            </value>
        </param>
    </params>
</methodCall>

Thank you

--
Ok, I got error, because I was sending request without authentication array,

How to add array arguments this to request?
How can I print on stdio generated xml form request?

xmlrpc proc-macro

Hello, not really an issue, but this may be of interest to users of this crate.

I've written a quick proof-of-concept proc-macro helper, which let us define remote rpc methods as rust trait methods. This is very helpful to make sure we call the right arguments in the right order thus taking advantage of type checking on rpc calls.

The repository is here, this is my first time writing a proc-macro, but it shows potential for improving and making safer APIs.

https://github.com/fungos/xmlrpc-proc

How to send a request?

In Request::call, the request is always posted to the "/" URL.

How does one make an XMLRPC request to a remote server? From reading the source for hyper, I didn't see any straightforward way to bind a hyper::client::Client to a particular base URL... it looks to me like the Client expects absolute URLs to be passed to get(), post(), etc., not relative URLs.

I am thinking that either Request::call needs to be modified to accept a target URL as a parameter, or if the method is actually correct as-is, some example code is needed for how to use it.

Also, I think that the Content-Type header should get set in the post():

.header(ContentType(Mime(TopLevel::Text, SubLevel::Xml, vec![(Attr::Charset, Value::Utf8)])))

Hide error details

The crate currently (as of 0.8.0) publicly exports 2 error enums: RequestError and ParseError. I plan to make all parser details private again (without compromising on features or flexibility, of course), which allows us to make ParseError private (again). We should define a newtype struct hiding the different error variants.

empty strings lead to UnexpectedXML error

Consider the following XML snippet: <member><name>foo</name><value><string/></value></member> (got that from an actual xml-rpc API)

I would expect it to become a map entry like "foo" : "" but instead it gives me an error Err(ParseError(UnexpectedXml { expected: "expected characters", position: 1:531 })) (the position is the start of <string/>)

I guess the problem is the assumption that strings have to contain a XmlEvent::Character at https://github.com/jonas-schievink/xml-rpc-rs/blob/master/src/parser.rs#L201

Very slow download

xml-rs is freaking slow so that it slows down download rates for any connection with more than like 1 MBit/s (even locally!).

I successfully worked around that by implementing and using a Transport wrapper that just adds buffering to the stream:

struct BufTransport {
    inner: RequestBuilder,
}

impl Transport for BufTransport {
    type Stream = BufReader<<RequestBuilder as Transport>::Stream>;

    fn transmit(self, request: &Request<'_>) -> Result<Self::Stream, Box<dyn Error + Send + Sync>> {
        self.inner.transmit(request).map(BufReader::new)
    }
}

(RequestBuilder could be a generic Read of course)

As long as xml-rpc-rs sticks with xml-rs, I guess it's a good idea to use a BufReader in general. I can prepare a pull request if you like.

Parse XML request from str

Hello,

Would you accept a PR for a function for parsing a string to build an XML request? I'm looking to create an XML server and would like to leverage common types if possible. I'd imagine it'd make the most sense to implement the std::str::FromStr trait, but open to suggestions on that.

Thanks!

Old hyper version

This crate is currently using hyper 0.10.*, which is the non async version.

Personally I would prefer if the crate would not switch to an async API as it just complicates usage (for me) and doesn't provide many gains except if someone was making tons of XML RPC calls, but it is an option and maybe wished for.

Another option would be switching to the reqwest crate which uses a recent version of Hyper behind the scenes, but most importantly cookies are missing yet and this might be a requirement here?

Reexport reqwest

If a downstream crate wants to send a request and can't just use call_url, it might be handy if we reexport reqwest so they don't have to add the correct version to Cargo.toml.

Need to figure out if this is a good idea and a common or recommended pattern.

Support non-UTF-8 encodings

We're currently using xml-rs to parse XML, which only supports UTF-8 (netvl/xml-rs#10).

To do this, either xml-rs needs to get support for other encodings or we have to switch to quick-xml which does support non-UTF-8 documents (but needs some work - it needs to be better tested and doesn't support encodings that aren't ASCII-compatible).

Note that the XML-spec mandates UTF-8 and UTF-16 support, so xml-rs is technically non-compliant (as is this crate).

Export the parser for direct usage

I've been working with this package for serialization and deserialization over an scgi transport, but that means I need to serialize and deserialize to something other than http. Thankfully, write_to_xml is implemented when http is disabled, but there's no way that I could currently find to deserialize from an xml document without using the http transport because the parser package doesn't appear to be exported.

"Expected Content-Length header, but none was found" on gzip compressed data

Some APIs may return gzip compressed data. In such a case, reqwest automatically removes the Content-Length header from the response as it's meaningless (see seanmonstar/reqwest#294). In such a case, xmlrpc still requires the Content-Length header in the response, although it can't be of any use and errors out with the message:

Err(Error(TransportError(StringError("expected Content-Length header, but none was found"))))

Example code that triggers the erroneous behaviour:


extern crate xmlrpc;

use xmlrpc::Request;
use xmlrpc::Value;
use std::collections::BTreeMap;

fn main() {
    const URL: &'static str = "http://api.ote.domrobot.com/xmlrpc/";

    let params = [("user", "foo"), ("pass", "bar")];
    let params: BTreeMap<String, Value> = params
        .into_iter()
        .map::<(String, Value), _>(|(k, v)| ((*k).to_owned(), (*v).into()))
        .collect();

    let req = Request::new("account.login").arg(Value::Struct(params));
    let res = req.call_url(URL);

    println!("Result: {:?}", res);
}

Cargo.toml:

[dependencies]
xmlrpc = "0.11"

As you can inspect (for example with tcpdump), the response originally contained the Content-Length header but it was thrown away by reqwest (see issue link above).

Don't use reqwest::blocking

I want to create an API service, and I see reqwest::blocking is used here. Is it possible to rewrite to use async/await instead?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.