#server-connection #codec #daemon #request #communication #unix #tokio

bin+lib daemon-engine

A helper package for writing unix rust daemons with IPC based clients

10 releases (5 breaking)

0.6.0 Apr 10, 2019
0.5.1 Mar 31, 2019
0.4.0 Jan 6, 2019
0.3.3 Dec 31, 2018
0.1.0 Aug 15, 2018

#1118 in Encoding


Used in remote-hal

MIT/Apache

51KB
879 lines

Rust Daemon

A library to simplify communication with daemons in rust, with the goal of hiding as much of the complexity of / minimizing the effort required to use tokio as much as possible.

This consists of a higher-level generic server and connection object to provide typed communication between components, implemented using tokio using codecs. This is designed to support arbitrary stream types, and both types have been implemented over TCPStream and UnixStream for convenience. If you find any other useful stream types to wrap, please open an issue or a PR!

A generic example codec is provided using serde and serde_json to establish a type-safe json interface for client-daemon communication, when using this codec the ENC and DEC types must implement serde Serialize and Deserialize traits, these may be implemented using serde_derive. It is intended that additional codecs will be added as they are required.

Status

GitHub tag Build Status Crates.io Docs.rs

Open Issues

Usage

See src/examples/server.rs and src/examples/connection.rs for an example server and client implementing a simple key-value store.

You can build these examples with cargo build --features examples, run the server with ./targets/debug/rustd-server and interact using ./targets/debug/rustd-client. rustd-client -k KEY fetches the value for a given key, rustd-client -k KEY -v VALUE sets the value of the given key, and rustd-client --help will display available arguments.

Client

extern crate daemon_engine;
use daemon_engine::Connection;

...

// Create client instance
let stream = UnixStream::connect(path.clone()).wait().unwrap();
let client = Connection::<_, JsonCodec<Test, Test, JsonError>>::new(stream);

// Split RX and TX
let (tx, rx) = client.split();

// Send something (remember to .wait())
tx.send(Request::Something).wait().unwrap();

// Receive something (also remember to wait)
rx.map(|resp| -> Result<(), DaemonError> {
    println!("Response: {:?}", resp);
    Ok(())
}).wait().next();

Server

extern crate tokio;
use tokio::prelude::*;
use tokio::{run, spawn};

extern crate daemon_engine;
use daemon_engine::Server;
use daemon_engine::codecs::json::{JsonCodec, JsonError};

...

let server_handle = future::lazy(move || {
    // Create server instance using the JSON codec, this must be executed from within a tokio context
    let mut server = Server::<_, JsonCodec<Request, Response, JsonError>>::new_unix(&server_path).unwrap();

    // Handle requests from clients
    s.incoming().unwrap().for_each(move |r| {
        println!("Request: {:?}", r.data());
        let data = r.data();
        match data {
            ...
            _ => {
                r.send(Response::Something(v.to_string()))
            }
        // Remember you have to .wait or otherwise prompt for send to occur
        }.wait().unwrap();
        Ok(())
    }).map_err(|_e| ());

    // do more stuff
    ...

    // Close the server when you're done
    s.close();

    Ok(())
});

// Create server task
tokio::run(server_handle);

If you have any questions, comments, or suggestions, feel free to open an issue or a pull request.

Dependencies

~10MB
~170K SLoC