Merge pull request #933 from ethcore/ipc-prs-client
IPC persistent client link
This commit is contained in:
commit
f1f81777cc
@ -303,6 +303,7 @@ fn push_client(
|
|||||||
{
|
{
|
||||||
push_client_struct(cx, builder, item, push);
|
push_client_struct(cx, builder, item, push);
|
||||||
push_client_implementation(cx, builder, dispatches, item, push);
|
push_client_implementation(cx, builder, dispatches, item, push);
|
||||||
|
push_with_socket_client_implementation(cx, builder, item, push);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns an expression with the body for single operation that is being sent to server
|
/// returns an expression with the body for single operation that is being sent to server
|
||||||
@ -485,6 +486,25 @@ fn implement_client_method(
|
|||||||
signature.unwrap()
|
signature.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn push_with_socket_client_implementation(
|
||||||
|
cx: &ExtCtxt,
|
||||||
|
builder: &aster::AstBuilder,
|
||||||
|
item: &Item,
|
||||||
|
push: &mut FnMut(Annotatable))
|
||||||
|
{
|
||||||
|
let client_ident = builder.id(format!("{}Client", item.ident.name.as_str()));
|
||||||
|
let implement = quote_item!(cx,
|
||||||
|
impl<S> ::ipc::WithSocket<S> for $client_ident<S> where S: ::ipc::IpcSocket {
|
||||||
|
fn init(socket: S) -> $client_ident<S> {
|
||||||
|
$client_ident {
|
||||||
|
socket: ::std::cell::RefCell::new(socket),
|
||||||
|
phantom: ::std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).unwrap();
|
||||||
|
push(Annotatable::Item(implement));
|
||||||
|
}
|
||||||
|
|
||||||
/// pushes full client side code for the original class exposed via ipc
|
/// pushes full client side code for the original class exposed via ipc
|
||||||
fn push_client_implementation(
|
fn push_client_implementation(
|
||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
@ -502,18 +522,11 @@ fn push_client_implementation(
|
|||||||
let item_ident = builder.id(format!("{}", item.ident.name.as_str()));
|
let item_ident = builder.id(format!("{}", item.ident.name.as_str()));
|
||||||
let implement = quote_item!(cx,
|
let implement = quote_item!(cx,
|
||||||
impl<S> $client_ident<S> where S: ::ipc::IpcSocket {
|
impl<S> $client_ident<S> where S: ::ipc::IpcSocket {
|
||||||
pub fn new(socket: S) -> $client_ident<S> {
|
|
||||||
$client_ident {
|
|
||||||
socket: ::std::cell::RefCell::new(socket),
|
|
||||||
phantom: ::std::marker::PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handshake(&self) -> Result<(), ::ipc::Error> {
|
pub fn handshake(&self) -> Result<(), ::ipc::Error> {
|
||||||
let payload = BinHandshake {
|
let payload = BinHandshake {
|
||||||
protocol_version: $item_ident::protocol_version().to_string(),
|
protocol_version: $item_ident::protocol_version().to_string(),
|
||||||
api_version: $item_ident::api_version().to_string(),
|
api_version: $item_ident::api_version().to_string(),
|
||||||
_reserved: vec![0u8, 64],
|
_reserved: vec![0u8; 64],
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut socket_ref = self.socket.borrow_mut();
|
let mut socket_ref = self.socket.borrow_mut();
|
||||||
|
@ -20,10 +20,11 @@ extern crate ethcore_ipc as ipc;
|
|||||||
extern crate nanomsg;
|
extern crate nanomsg;
|
||||||
#[macro_use] extern crate log;
|
#[macro_use] extern crate log;
|
||||||
|
|
||||||
pub use ipc::*;
|
pub use ipc::{WithSocket, IpcInterface, IpcConfig};
|
||||||
|
|
||||||
use std::sync::*;
|
use std::sync::*;
|
||||||
use nanomsg::{Socket, Protocol, Error, Endpoint, PollRequest, PollFd, PollInOut};
|
use nanomsg::{Socket, Protocol, Error, Endpoint, PollRequest, PollFd, PollInOut};
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
const POLL_TIMEOUT: isize = 100;
|
const POLL_TIMEOUT: isize = 100;
|
||||||
|
|
||||||
@ -34,6 +35,36 @@ pub struct Worker<S> where S: IpcInterface<S> {
|
|||||||
buf: Vec<u8>,
|
buf: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct GuardedSocket<S> where S: WithSocket<Socket> {
|
||||||
|
client: Arc<S>,
|
||||||
|
_endpoint: Endpoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Deref for GuardedSocket<S> where S: WithSocket<Socket> {
|
||||||
|
type Target = S;
|
||||||
|
|
||||||
|
fn deref(&self) -> &S {
|
||||||
|
&self.client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_client<S>(socket_addr: &str) -> Result<GuardedSocket<S>, SocketError> where S: WithSocket<Socket> {
|
||||||
|
let mut socket = try!(Socket::new(Protocol::Pair).map_err(|e| {
|
||||||
|
warn!(target: "ipc", "Failed to create ipc socket: {:?}", e);
|
||||||
|
SocketError::DuplexLink
|
||||||
|
}));
|
||||||
|
|
||||||
|
let endpoint = try!(socket.connect(socket_addr).map_err(|e| {
|
||||||
|
warn!(target: "ipc", "Failed to bind socket to address '{}': {:?}", socket_addr, e);
|
||||||
|
SocketError::DuplexLink
|
||||||
|
}));
|
||||||
|
|
||||||
|
Ok(GuardedSocket {
|
||||||
|
client: Arc::new(S::init(socket)),
|
||||||
|
_endpoint: endpoint,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SocketError {
|
pub enum SocketError {
|
||||||
DuplexLink
|
DuplexLink
|
||||||
@ -60,6 +91,7 @@ impl<S> Worker<S> where S: IpcInterface<S> {
|
|||||||
match socket.nb_read_to_end(&mut self.buf) {
|
match socket.nb_read_to_end(&mut self.buf) {
|
||||||
Ok(method_sign_len) => {
|
Ok(method_sign_len) => {
|
||||||
if method_sign_len >= 2 {
|
if method_sign_len >= 2 {
|
||||||
|
|
||||||
// method_num
|
// method_num
|
||||||
let method_num = self.buf[1] as u16 * 256 + self.buf[0] as u16;
|
let method_num = self.buf[1] as u16 * 256 + self.buf[0] as u16;
|
||||||
// payload
|
// payload
|
||||||
@ -113,7 +145,7 @@ impl<S> Worker<S> where S: IpcInterface<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod service_tests {
|
||||||
|
|
||||||
use super::Worker;
|
use super::Worker;
|
||||||
use ipc::*;
|
use ipc::*;
|
||||||
@ -150,10 +182,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IpcConfig for DummyService {}
|
||||||
|
|
||||||
fn dummy_write(addr: &str, buf: &[u8]) -> (Socket, Endpoint) {
|
fn dummy_write(addr: &str, buf: &[u8]) -> (Socket, Endpoint) {
|
||||||
let mut socket = Socket::new(Protocol::Pair).unwrap();
|
let mut socket = Socket::new(Protocol::Pair).unwrap();
|
||||||
let endpoint = socket.connect(addr).unwrap();
|
let endpoint = socket.connect(addr).unwrap();
|
||||||
//thread::sleep_ms(10);
|
|
||||||
socket.write(buf).unwrap();
|
socket.write(buf).unwrap();
|
||||||
(socket, endpoint)
|
(socket, endpoint)
|
||||||
}
|
}
|
||||||
|
@ -9,3 +9,4 @@ license = "GPL-3.0"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
ethcore-devtools = { path = "../../devtools" }
|
ethcore-devtools = { path = "../../devtools" }
|
||||||
semver = "0.2.0"
|
semver = "0.2.0"
|
||||||
|
nanomsg = "0.5.0"
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::marker::Sync;
|
use std::marker::Sync;
|
||||||
use std::sync::atomic::*;
|
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
|
|
||||||
pub struct Handshake {
|
pub struct Handshake {
|
||||||
@ -49,7 +48,7 @@ pub enum Error {
|
|||||||
HandshakeFailed,
|
HandshakeFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait IpcInterface<T> where T: IpcConfig {
|
pub trait IpcInterface<T>: IpcConfig {
|
||||||
/// reads the message from io, dispatches the call and returns serialized result
|
/// reads the message from io, dispatches the call and returns serialized result
|
||||||
fn dispatch<R>(&self, r: &mut R) -> Vec<u8> where R: Read;
|
fn dispatch<R>(&self, r: &mut R) -> Vec<u8> where R: Read;
|
||||||
|
|
||||||
@ -72,6 +71,7 @@ pub fn invoke<W>(method_num: u16, params: &Option<Vec<u8>>, w: &mut W) where W:
|
|||||||
if params.is_some() {
|
if params.is_some() {
|
||||||
buf[2..buf_len].clone_from_slice(params.as_ref().unwrap());
|
buf[2..buf_len].clone_from_slice(params.as_ref().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.write(&buf).unwrap() != buf_len
|
if w.write(&buf).unwrap() != buf_len
|
||||||
{
|
{
|
||||||
// if write was inconsistent
|
// if write was inconsistent
|
||||||
@ -83,5 +83,12 @@ pub fn invoke<W>(method_num: u16, params: &Option<Vec<u8>>, w: &mut W) where W:
|
|||||||
pub trait IpcSocket: Read + Write + Sync {
|
pub trait IpcSocket: Read + Write + Sync {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IpcSocket for ::devtools::TestSocket {
|
|
||||||
|
pub trait WithSocket<S: IpcSocket> {
|
||||||
|
fn init(socket: S) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl IpcSocket for ::devtools::TestSocket {}
|
||||||
|
|
||||||
|
impl IpcSocket for ::nanomsg::Socket {}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
extern crate ethcore_devtools as devtools;
|
extern crate ethcore_devtools as devtools;
|
||||||
extern crate semver;
|
extern crate semver;
|
||||||
|
extern crate nanomsg;
|
||||||
|
|
||||||
pub mod interface;
|
pub mod interface;
|
||||||
pub use interface::{IpcInterface, IpcSocket, invoke, IpcConfig, Handshake, Error};
|
pub use interface::{IpcInterface, IpcSocket, invoke, IpcConfig, Handshake, Error, WithSocket};
|
||||||
|
@ -13,6 +13,9 @@ bincode = "*"
|
|||||||
serde = "0.7.0"
|
serde = "0.7.0"
|
||||||
ethcore-devtools = { path = "../../devtools" }
|
ethcore-devtools = { path = "../../devtools" }
|
||||||
semver = "0.2.0"
|
semver = "0.2.0"
|
||||||
|
nanomsg = "0.5.0"
|
||||||
|
ethcore-ipc-nano = { path = "../nano" }
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
syntex = "0.30.0"
|
syntex = "0.30.0"
|
||||||
|
@ -63,7 +63,7 @@ mod tests {
|
|||||||
fn call_service_client() {
|
fn call_service_client() {
|
||||||
let mut socket = TestSocket::new();
|
let mut socket = TestSocket::new();
|
||||||
socket.read_buffer = vec![0, 0, 0, 10];
|
socket.read_buffer = vec![0, 0, 0, 10];
|
||||||
let service_client = ServiceClient::new(socket);
|
let service_client = ServiceClient::init(socket);
|
||||||
|
|
||||||
let result = service_client.commit(5);
|
let result = service_client.commit(5);
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ mod tests {
|
|||||||
fn call_service_client_optional() {
|
fn call_service_client_optional() {
|
||||||
let mut socket = TestSocket::new();
|
let mut socket = TestSocket::new();
|
||||||
socket.read_buffer = vec![0, 0, 0, 10];
|
socket.read_buffer = vec![0, 0, 0, 10];
|
||||||
let service_client = ServiceClient::new(socket);
|
let service_client = ServiceClient::init(socket);
|
||||||
|
|
||||||
let result = service_client.rollback(Some(5), 10);
|
let result = service_client.rollback(Some(5), 10);
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ mod tests {
|
|||||||
fn call_service_client_handshake() {
|
fn call_service_client_handshake() {
|
||||||
let mut socket = TestSocket::new();
|
let mut socket = TestSocket::new();
|
||||||
socket.read_buffer = vec![1];
|
socket.read_buffer = vec![1];
|
||||||
let service_client = ServiceClient::new(socket);
|
let service_client = ServiceClient::init(socket);
|
||||||
|
|
||||||
let result = service_client.handshake();
|
let result = service_client.handshake();
|
||||||
|
|
||||||
|
109
ipc/tests/over_nano.rs
Normal file
109
ipc/tests/over_nano.rs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::super::service::*;
|
||||||
|
use nanoipc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::io::{Write, Read};
|
||||||
|
use std::sync::atomic::{Ordering, AtomicBool};
|
||||||
|
|
||||||
|
fn dummy_write(addr: &str, buf: &[u8]) -> (::nanomsg::Socket, ::nanomsg::Endpoint) {
|
||||||
|
let mut socket = ::nanomsg::Socket::new(::nanomsg::Protocol::Pair).unwrap();
|
||||||
|
let endpoint = socket.connect(addr).unwrap();
|
||||||
|
socket.write(buf).unwrap();
|
||||||
|
(socket, endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn init_worker(addr: &str) -> nanoipc::Worker<Service> {
|
||||||
|
let mut worker = nanoipc::Worker::<Service>::new(Arc::new(Service::new()));
|
||||||
|
worker.add_duplex(addr).unwrap();
|
||||||
|
worker
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_create_client() {
|
||||||
|
let client = nanoipc::init_client::<ServiceClient<_>>("ipc:///tmp/parity-nano-test10.ipc");
|
||||||
|
assert!(client.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_call_handshake() {
|
||||||
|
let url = "ipc:///tmp/parity-test-nano-20.ipc";
|
||||||
|
let worker_should_exit = Arc::new(AtomicBool::new(false));
|
||||||
|
let worker_is_ready = Arc::new(AtomicBool::new(false));
|
||||||
|
let c_worker_should_exit = worker_should_exit.clone();
|
||||||
|
let c_worker_is_ready = worker_is_ready.clone();
|
||||||
|
|
||||||
|
::std::thread::spawn(move || {
|
||||||
|
let mut worker = init_worker(url);
|
||||||
|
while !c_worker_should_exit.load(Ordering::Relaxed) {
|
||||||
|
worker.poll();
|
||||||
|
c_worker_is_ready.store(true, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
while !worker_is_ready.load(Ordering::Relaxed) { }
|
||||||
|
let client = nanoipc::init_client::<ServiceClient<_>>(url).unwrap();
|
||||||
|
|
||||||
|
let hs = client.handshake();
|
||||||
|
|
||||||
|
worker_should_exit.store(true, Ordering::Relaxed);
|
||||||
|
assert!(hs.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_receive_dummy_writes_in_thread() {
|
||||||
|
let url = "ipc:///tmp/parity-test-nano-30.ipc";
|
||||||
|
let worker_should_exit = Arc::new(AtomicBool::new(false));
|
||||||
|
let worker_is_ready = Arc::new(AtomicBool::new(false));
|
||||||
|
let c_worker_should_exit = worker_should_exit.clone();
|
||||||
|
let c_worker_is_ready = worker_is_ready.clone();
|
||||||
|
|
||||||
|
::std::thread::spawn(move || {
|
||||||
|
let mut worker = init_worker(url);
|
||||||
|
while !c_worker_should_exit.load(Ordering::Relaxed) {
|
||||||
|
worker.poll();
|
||||||
|
c_worker_is_ready.store(true, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
while !worker_is_ready.load(Ordering::Relaxed) { }
|
||||||
|
|
||||||
|
let (mut _s, _e) = dummy_write(url, &vec![0, 0,
|
||||||
|
// protocol version
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 5, b'1', b'.', b'0', b'.', b'0',
|
||||||
|
// api version
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 5, b'1', b'.', b'0', b'.', b'0',
|
||||||
|
// reserved
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 64,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
let mut buf = vec![0u8;1];
|
||||||
|
_s.read(&mut buf).unwrap();
|
||||||
|
assert_eq!(1, buf.len());
|
||||||
|
assert_eq!(1, buf[0]);
|
||||||
|
|
||||||
|
worker_should_exit.store(true, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -19,6 +19,9 @@ extern crate ethcore_ipc as ipc;
|
|||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate ethcore_devtools as devtools;
|
extern crate ethcore_devtools as devtools;
|
||||||
extern crate semver;
|
extern crate semver;
|
||||||
|
extern crate nanomsg;
|
||||||
|
extern crate ethcore_ipc_nano as nanoipc;
|
||||||
|
|
||||||
pub mod service;
|
pub mod service;
|
||||||
mod examples;
|
mod examples;
|
||||||
|
mod over_nano;
|
||||||
|
Loading…
Reference in New Issue
Block a user