Eider

Eider is an object-oriented, asynchronous, late-binding, web-first, polyglot RPC protocol. Eider is also the name of the reference implementations in Python and JavaScript.

Out of the box, Eider uses WebSockets for transport and JSON or MessagePack for serialization. The core protocol and concepts are transport- and format-neutral.

With Eider, developing applications that combine server-side, client-side, and in-browser code is duck soup!

Installation

Python

Works in CPython 3.4+ and PyPy3. Requires either aiohttp or websockets. Includes elective support for MessagePack using msgpack-python, if available.

pip install eider
pip install msgpack  # optional

# either:
pip install aiohttp  # recommended
pip install websockets  # slower

You can also check out the source code on GitHub.

JavaScript

Works in Node.js 6+, modern browsers, and any other environment that supports ES6.

No external libraries are strictly required. If installed, the uws or ws libraries may be used for WebSocket creation in Node.js. Optional dependencies are msgpack-lite for MessagePack encoding and weak for implicit remote garbage collection.

npm install eider-rpc

# either:
npm install uws  # recommended
npm install ws  # slower

For the browser: eider-rpc.min.js.

You can also check out the source code on GitHub.

C++

The eider-pybind11 project provides a simple header file that can enable classes implemented in C++ to be served over Eider connections using pybind11.

Getting Started

Here is a simple server in Python (simple_server.py):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import eider

class DuckTester(eider.LocalRoot):
    
    def is_it_a_duck(self, obj):
        return (obj['looks'] == 'like a duck' and
            obj['swims'] == 'like a duck' and
            obj['quacks'] == 'like a duck')

eider.serve(12345, root=DuckTester)

And here is an equivalent server in JavaScript (simple_server.js):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let Eider = require('eider-rpc');

class DuckTester extends Eider.LocalRoot {
    is_it_a_duck(obj) {
        return obj.looks === 'like a duck' &&
            obj.swims === 'like a duck' &&
            obj.quacks === 'like a duck';
    }
}

Eider.serve(12345, {root: DuckTester});

Here is how the core of the Python server could be written in C++ (simple_server.cpp):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <eider_pybind11.hpp>

using namespace eider_pybind11;

struct DuckTester : LocalRoot {
    using LocalRoot::LocalRoot;

    bool is_it_a_duck(py::object obj) {
        return std::string(py::str(obj["looks"])) == "like a duck" &&
            std::string(py::str(obj["swims"])) == "like a duck" &&
            std::string(py::str(obj["quacks"])) == "like a duck";
    }
};

PYBIND11_MODULE(ducktest, m) {
    bind(m);

    py::class_<DuckTester, LocalRoot>(m, "DuckTester")
        .def(py::init<LocalSession>())
        .def("is_it_a_duck", &DuckTester::is_it_a_duck);
}

Here is a client in Python (simple_client.py):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import eider

how = 'like a duck'
obj = {'looks': how, 'swims': how, 'quacks': how}

with eider.BlockingConnection('ws://localhost:12345') as conn:
    with conn.create_session() as duck_tester:
        is_duck = duck_tester.is_it_a_duck(obj)

print("It's probably " + ("" if is_duck else "NOT ") + "a duck.")

And here is an equivalent client in JavaScript (simple_client.html):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<script src="eider-rpc.min.js"></script>
<script type="text/javascript">
    let how = 'like a duck';
    let obj = {looks: how, swims: how, quacks: how};
    Eider.using(Eider.connect('ws://localhost:12345'), conn =>
        Eider.using(conn.createSession(), duckTester =>
            duckTester.is_it_a_duck(obj)
        )
    ).then(isDuck => {
        alert("It's probably " + (isDuck ? '' : 'NOT ') + 'a duck.');
    });
</script>

And finally, here is a Python 3.5+ client using non-blocking APIs (simple_client_async.py):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import asyncio, eider

how = 'like a duck'
obj = {'looks': how, 'swims': how, 'quacks': how}

async def ducktest():
    async with eider.Connection('ws://localhost:12345') as conn:
        async with conn.create_session() as duck_tester:
            is_duck = await duck_tester.is_it_a_duck(obj)

    print("It's probably " + ("" if is_duck else "NOT ") + "a duck.")

asyncio.get_event_loop().run_until_complete(ducktest())

Blame

Eider began as an internal software library at ON Semiconductor and was open-sourced under the Apache License 2.0 in April 2017.

Bart Robinson is the original author and current maintainer.

King Eider photo by Ron Knight (CC BY 2.0).