Add rustfmt to travis (#137)

* RFC styling-based `rustfmt.toml`

* Add rustfmt to travis

* Remove rustfmt.toml and actually fix too-long lines instead of ignoring them
This commit is contained in:
Jef
2018-03-07 16:20:55 +01:00
committed by GitHub
parent e8d29af359
commit 5217e29fab
54 changed files with 5801 additions and 5572 deletions

View File

@ -10,6 +10,7 @@ before_install:
- if [ "${TRAVIS_OS_NAME}" == "linux" ]; then - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6';
fi fi
- rustup component add rustfmt-preview
cache: cargo cache: cargo
@ -19,6 +20,6 @@ matrix:
- rust: stable - rust: stable
script: script:
- cargo fmt -- --write-mode=checkstyle
- cargo check --all - cargo check --all
- cargo test --all - cargo test --all

View File

@ -221,9 +221,8 @@ impl<B: Array> CircularBuffer<B> {
/// when the slice goes out of scope), if you're using non-`Drop` types you can use /// when the slice goes out of scope), if you're using non-`Drop` types you can use
/// `pop_slice_leaky`. /// `pop_slice_leaky`.
pub fn pop_slice(&mut self) -> Option<OwnedSlice<B::Item>> { pub fn pop_slice(&mut self) -> Option<OwnedSlice<B::Item>> {
self.pop_slice_leaky().map( self.pop_slice_leaky()
|x| unsafe { OwnedSlice::new(x) }, .map(|x| unsafe { OwnedSlice::new(x) })
)
} }
/// Pop a slice containing the maximum possible contiguous number of elements. Since this buffer /// Pop a slice containing the maximum possible contiguous number of elements. Since this buffer
@ -435,9 +434,9 @@ impl<B: Array> CircularBuffer<B> {
/// Get a borrow to an element at an index unsafely (behaviour is undefined if the index is out /// Get a borrow to an element at an index unsafely (behaviour is undefined if the index is out
/// of bounds). /// of bounds).
pub unsafe fn get_unchecked(&self, index: usize) -> &B::Item { pub unsafe fn get_unchecked(&self, index: usize) -> &B::Item {
&*self.buffer.ptr().offset( &*self.buffer
((index + self.start) % B::size()) as isize, .ptr()
) .offset(((index + self.start) % B::size()) as isize)
} }
/// Get a mutable borrow to an element at an index safely (if the index is out of bounds, return /// Get a mutable borrow to an element at an index safely (if the index is out of bounds, return
@ -453,16 +452,15 @@ impl<B: Array> CircularBuffer<B> {
/// Get a mutable borrow to an element at an index unsafely (behaviour is undefined if the index /// Get a mutable borrow to an element at an index unsafely (behaviour is undefined if the index
/// is out of bounds). /// is out of bounds).
pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut B::Item { pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut B::Item {
&mut *self.buffer.ptr_mut().offset( &mut *self.buffer
((index + self.start) % B::size()) as .ptr_mut()
isize, .offset(((index + self.start) % B::size()) as isize)
)
} }
/// Removes the first `by` elements of the start of the buffer. /// Removes the first `by` elements of the start of the buffer.
/// ///
/// # Panic /// # Panic
/// ///
/// Panics if `by` is superior to the number of elements in the buffer. /// Panics if `by` is superior to the number of elements in the buffer.
// This is not unsafe because it can only leak data, not cause uninit to be read. // This is not unsafe because it can only leak data, not cause uninit to be read.
pub fn advance(&mut self, by: usize) { pub fn advance(&mut self, by: usize) {
@ -482,8 +480,7 @@ impl<B: Array> std::ops::Index<usize> for CircularBuffer<B> {
} else { } else {
panic!( panic!(
"index out of bounds: the len is {} but the index is {}", "index out of bounds: the len is {} but the index is {}",
self.len, self.len, index
index
); );
} }
} }
@ -499,8 +496,7 @@ impl<B: Array> std::ops::IndexMut<usize> for CircularBuffer<B> {
} else { } else {
panic!( panic!(
"index out of bounds: the len is {} but the index is {}", "index out of bounds: the len is {} but the index is {}",
len, len, index
index
); );
} }
} }
@ -624,7 +620,9 @@ where
/// ```rust /// ```rust
/// use circular_buffer::CircularBuffer; /// use circular_buffer::CircularBuffer;
/// ///
/// let result = CircularBuffer::<[usize; 5]>::from_slice_prefix(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 20]); /// let result = CircularBuffer::<[usize; 5]>::from_slice_prefix(
/// &[1, 2, 3, 4, 5, 6, 7, 8, 9, 20]
/// );
/// assert_eq!(result, (CircularBuffer::from_array([1, 2, 3, 4, 5]), 5)); /// assert_eq!(result, (CircularBuffer::from_array([1, 2, 3, 4, 5]), 5));
/// ``` /// ```
pub fn from_slice_prefix(slice: &[B::Item]) -> (Self, usize) { pub fn from_slice_prefix(slice: &[B::Item]) -> (Self, usize) {

View File

@ -23,11 +23,11 @@
use Datastore; use Datastore;
use chashmap::{CHashMap, WriteGuard}; use chashmap::{CHashMap, WriteGuard};
use futures::Future; use futures::Future;
use futures::stream::{Stream, iter_ok}; use futures::stream::{iter_ok, Stream};
use query::{Query, naive_apply_query}; use query::{naive_apply_query, Query};
use serde::Serialize; use serde::Serialize;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde_json::{Map, from_value, to_value, from_reader, to_writer}; use serde_json::{from_reader, from_value, to_value, to_writer, Map};
use serde_json::value::Value; use serde_json::value::Value;
use std::borrow::Cow; use std::borrow::Cow;
use std::fs; use std::fs;
@ -41,268 +41,291 @@ use tempfile::NamedTempFile;
/// Implementation of `Datastore` that uses a single plain JSON file. /// Implementation of `Datastore` that uses a single plain JSON file.
pub struct JsonFileDatastore<T> pub struct JsonFileDatastore<T>
where T: Serialize + DeserializeOwned + Clone where
T: Serialize + DeserializeOwned + Clone,
{ {
path: PathBuf, path: PathBuf,
content: CHashMap<String, T>, content: CHashMap<String, T>,
} }
impl<T> JsonFileDatastore<T> impl<T> JsonFileDatastore<T>
where T: Serialize + DeserializeOwned + Clone where
T: Serialize + DeserializeOwned + Clone,
{ {
/// Opens or creates the datastore. If the path refers to an existing path, then this function /// Opens or creates the datastore. If the path refers to an existing path, then this function
/// will attempt to load an existing set of values from it (which can result in an error). /// will attempt to load an existing set of values from it (which can result in an error).
/// Otherwise if the path doesn't exist, a new empty datastore will be created. /// Otherwise if the path doesn't exist, a new empty datastore will be created.
pub fn new<P>(path: P) -> Result<JsonFileDatastore<T>, IoError> pub fn new<P>(path: P) -> Result<JsonFileDatastore<T>, IoError>
where P: Into<PathBuf> where
{ P: Into<PathBuf>,
let path = path.into(); {
let path = path.into();
if !path.exists() { if !path.exists() {
return Ok(JsonFileDatastore { return Ok(JsonFileDatastore {
path: path, path: path,
content: CHashMap::new(), content: CHashMap::new(),
}); });
} }
let content = { let content = {
let mut file = fs::File::open(&path)?; let mut file = fs::File::open(&path)?;
// We want to support empty files (and treat them as an empty recordset). Unfortunately // We want to support empty files (and treat them as an empty recordset). Unfortunately
// `serde_json` will always produce an error if we do this ("unexpected EOF at line 0 // `serde_json` will always produce an error if we do this ("unexpected EOF at line 0
// column 0"). Therefore we start by reading one byte from the file in order to check // column 0"). Therefore we start by reading one byte from the file in order to check
// for EOF. // for EOF.
let mut first_byte = [0]; let mut first_byte = [0];
if file.read(&mut first_byte)? == 0 { if file.read(&mut first_byte)? == 0 {
// File is empty. // File is empty.
CHashMap::new() CHashMap::new()
} else { } else {
match from_reader::<_, Value>(Cursor::new(first_byte).chain(file)) { match from_reader::<_, Value>(Cursor::new(first_byte).chain(file)) {
Ok(Value::Null) => CHashMap::new(), Ok(Value::Null) => CHashMap::new(),
Ok(Value::Object(map)) => { Ok(Value::Object(map)) => {
let mut out = CHashMap::with_capacity(map.len()); let mut out = CHashMap::with_capacity(map.len());
for (key, value) in map.into_iter() { for (key, value) in map.into_iter() {
let value = match from_value(value) { let value = match from_value(value) {
Ok(v) => v, Ok(v) => v,
Err(err) => return Err(IoError::new(IoErrorKind::InvalidData, err)), Err(err) => return Err(IoError::new(IoErrorKind::InvalidData, err)),
}; };
out.insert(key, value); out.insert(key, value);
} }
out out
} }
Ok(_) => { Ok(_) => {
return Err(IoError::new(IoErrorKind::InvalidData, "expected JSON object")); return Err(IoError::new(
} IoErrorKind::InvalidData,
Err(err) => { "expected JSON object",
return Err(IoError::new(IoErrorKind::InvalidData, err)); ));
} }
} Err(err) => {
} return Err(IoError::new(IoErrorKind::InvalidData, err));
}; }
}
}
};
Ok(JsonFileDatastore { path: path, content: content }) Ok(JsonFileDatastore {
} path: path,
content: content,
})
}
/// Flushes the content of the datastore to the disk. /// Flushes the content of the datastore to the disk.
/// ///
/// This function can only fail in case of a disk access error. If an error occurs, any change /// This function can only fail in case of a disk access error. If an error occurs, any change
/// to the datastore that was performed since the last successful flush will be lost. No data /// to the datastore that was performed since the last successful flush will be lost. No data
/// will be corrupted. /// will be corrupted.
pub fn flush(&self) -> Result<(), IoError> pub fn flush(&self) -> Result<(), IoError>
where T: Clone where
{ T: Clone,
// Create a temporary file in the same directory as the destination, which avoids the {
// problem of having a file cleaner delete our file while we use it. // Create a temporary file in the same directory as the destination, which avoids the
let self_path_parent = self.path // problem of having a file cleaner delete our file while we use it.
.parent() let self_path_parent = self.path.parent().ok_or(IoError::new(
.ok_or(IoError::new( IoErrorKind::Other,
IoErrorKind::Other, "couldn't get parent directory of destination",
"couldn't get parent directory of destination", ))?;
))?; let mut temporary_file = NamedTempFile::new_in(self_path_parent)?;
let mut temporary_file = NamedTempFile::new_in(self_path_parent)?;
let content = self.content.clone().into_iter(); let content = self.content.clone().into_iter();
to_writer( to_writer(
&mut temporary_file, &mut temporary_file,
&content.map(|(k, v)| (k, to_value(v).unwrap())).collect::<Map<_, _>>(), &content
)?; .map(|(k, v)| (k, to_value(v).unwrap()))
temporary_file.sync_data()?; .collect::<Map<_, _>>(),
)?;
temporary_file.sync_data()?;
// Note that `persist` will fail if we try to persist across filesystems. However that // Note that `persist` will fail if we try to persist across filesystems. However that
// shouldn't happen since we created the temporary file in the same directory as the final // shouldn't happen since we created the temporary file in the same directory as the final
// path. // path.
temporary_file.persist(&self.path)?; temporary_file.persist(&self.path)?;
Ok(()) Ok(())
} }
} }
impl<'a, T> Datastore<T> for &'a JsonFileDatastore<T> impl<'a, T> Datastore<T> for &'a JsonFileDatastore<T>
where T: Clone + Serialize + DeserializeOwned + Default + PartialOrd + 'static where
T: Clone + Serialize + DeserializeOwned + Default + PartialOrd + 'static,
{ {
type Entry = JsonFileDatastoreEntry<'a, T>; type Entry = JsonFileDatastoreEntry<'a, T>;
type QueryResult = Box<Stream<Item = (String, T), Error = IoError> + 'a>; type QueryResult = Box<Stream<Item = (String, T), Error = IoError> + 'a>;
#[inline] #[inline]
fn lock(self, key: Cow<str>) -> Option<Self::Entry> { fn lock(self, key: Cow<str>) -> Option<Self::Entry> {
self.content.get_mut(&key.into_owned()).map(JsonFileDatastoreEntry) self.content
} .get_mut(&key.into_owned())
.map(JsonFileDatastoreEntry)
}
#[inline] #[inline]
fn lock_or_create(self, key: Cow<str>) -> Self::Entry { fn lock_or_create(self, key: Cow<str>) -> Self::Entry {
loop { loop {
self.content.upsert(key.clone().into_owned(), || Default::default(), |_| {}); self.content
.upsert(key.clone().into_owned(), || Default::default(), |_| {});
// There is a slight possibility that another thread will delete our value in this // There is a slight possibility that another thread will delete our value in this
// small interval. If this happens, we just loop and reinsert the value again until // small interval. If this happens, we just loop and reinsert the value again until
// we can acquire a lock. // we can acquire a lock.
if let Some(v) = self.content.get_mut(&key.clone().into_owned()) { if let Some(v) = self.content.get_mut(&key.clone().into_owned()) {
return JsonFileDatastoreEntry(v); return JsonFileDatastoreEntry(v);
} }
} }
} }
#[inline] #[inline]
fn put(self, key: Cow<str>, value: T) { fn put(self, key: Cow<str>, value: T) {
self.content.insert(key.into_owned(), value); self.content.insert(key.into_owned(), value);
} }
#[inline] #[inline]
fn get(self, key: &str) -> Option<T> { fn get(self, key: &str) -> Option<T> {
self.content.get(&key.to_owned()).map(|v| v.clone()) self.content.get(&key.to_owned()).map(|v| v.clone())
} }
#[inline] #[inline]
fn has(self, key: &str) -> bool { fn has(self, key: &str) -> bool {
self.content.contains_key(&key.to_owned()) self.content.contains_key(&key.to_owned())
} }
#[inline] #[inline]
fn delete(self, key: &str) -> Option<T> { fn delete(self, key: &str) -> Option<T> {
self.content.remove(&key.to_owned()) self.content.remove(&key.to_owned())
} }
fn query(self, query: Query<T>) -> Self::QueryResult { fn query(self, query: Query<T>) -> Self::QueryResult {
let content = self.content.clone(); let content = self.content.clone();
let keys_only = query.keys_only; let keys_only = query.keys_only;
let content_stream = iter_ok(content.into_iter().filter_map(|(key, value)| { let content_stream = iter_ok(content.into_iter().filter_map(|(key, value)| {
// Skip values that are malformed. // Skip values that are malformed.
let value = if keys_only { Default::default() } else { value }; let value = if keys_only { Default::default() } else { value };
Some((key, value)) Some((key, value))
})); }));
// `content_stream` reads from the content of the `Mutex`, so we need to clone the data // `content_stream` reads from the content of the `Mutex`, so we need to clone the data
// into a `Vec` before returning. // into a `Vec` before returning.
let collected = naive_apply_query(content_stream, query).collect().wait().expect( let collected = naive_apply_query(content_stream, query)
"can only fail if either `naive_apply_query` or `content_stream` produce \ .collect()
an error, which cann't happen", .wait()
); .expect(
let output_stream = iter_ok(collected.into_iter()); "can only fail if either `naive_apply_query` or `content_stream` produce \
Box::new(output_stream) as Box<_> an error, which cann't happen",
} );
let output_stream = iter_ok(collected.into_iter());
Box::new(output_stream) as Box<_>
}
} }
impl<T> Drop for JsonFileDatastore<T> impl<T> Drop for JsonFileDatastore<T>
where T: Serialize + DeserializeOwned + Clone where
T: Serialize + DeserializeOwned + Clone,
{ {
#[inline] #[inline]
fn drop(&mut self) { fn drop(&mut self) {
// Unfortunately there's not much we can do here in case of an error, as panicking would be // Unfortunately there's not much we can do here in case of an error, as panicking would be
// very bad. Similar to `File`, the user should take care to call `flush()` before dropping // very bad. Similar to `File`, the user should take care to call `flush()` before dropping
// the datastore. // the datastore.
// //
// If an error happens here, any change since the last successful flush will be lost, but // If an error happens here, any change since the last successful flush will be lost, but
// the data will not be corrupted. // the data will not be corrupted.
let _ = self.flush(); let _ = self.flush();
} }
} }
/// Implementation of `Datastore` that uses a single plain JSON file. /// Implementation of `Datastore` that uses a single plain JSON file.
pub struct JsonFileDatastoreEntry<'a, T>(WriteGuard<'a, String, T>) where T: 'a; pub struct JsonFileDatastoreEntry<'a, T>(WriteGuard<'a, String, T>)
where
T: 'a;
impl<'a, T> Deref for JsonFileDatastoreEntry<'a, T> impl<'a, T> Deref for JsonFileDatastoreEntry<'a, T>
where T: 'a where
T: 'a,
{ {
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {
&*self.0 &*self.0
} }
} }
impl<'a, T> DerefMut for JsonFileDatastoreEntry<'a, T> impl<'a, T> DerefMut for JsonFileDatastoreEntry<'a, T>
where T: 'a where
T: 'a,
{ {
fn deref_mut(&mut self) -> &mut T { fn deref_mut(&mut self) -> &mut T {
&mut *self.0 &mut *self.0
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use {Query, Order, Filter, FilterTy, FilterOp}; use {Filter, FilterOp, FilterTy, Order, Query};
use Datastore; use Datastore;
use JsonFileDatastore; use JsonFileDatastore;
use futures::{Future, Stream}; use futures::{Future, Stream};
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
#[test] #[test]
fn open_and_flush() { fn open_and_flush() {
let temp_file = NamedTempFile::new().unwrap(); let temp_file = NamedTempFile::new().unwrap();
let datastore = JsonFileDatastore::<Vec<u8>>::new(temp_file.path()).unwrap(); let datastore = JsonFileDatastore::<Vec<u8>>::new(temp_file.path()).unwrap();
datastore.flush().unwrap(); datastore.flush().unwrap();
} }
#[test] #[test]
fn values_store_and_reload() { fn values_store_and_reload() {
let temp_file = NamedTempFile::new().unwrap(); let temp_file = NamedTempFile::new().unwrap();
let datastore = JsonFileDatastore::<Vec<u8>>::new(temp_file.path()).unwrap(); let datastore = JsonFileDatastore::<Vec<u8>>::new(temp_file.path()).unwrap();
datastore.put("foo".into(), vec![1, 2, 3]); datastore.put("foo".into(), vec![1, 2, 3]);
datastore.put("bar".into(), vec![0, 255, 127]); datastore.put("bar".into(), vec![0, 255, 127]);
datastore.flush().unwrap(); datastore.flush().unwrap();
drop(datastore); drop(datastore);
let reload = JsonFileDatastore::<Vec<u8>>::new(temp_file.path()).unwrap(); let reload = JsonFileDatastore::<Vec<u8>>::new(temp_file.path()).unwrap();
assert_eq!(reload.get("bar").unwrap(), &[0, 255, 127]); assert_eq!(reload.get("bar").unwrap(), &[0, 255, 127]);
assert_eq!(reload.get("foo").unwrap(), &[1, 2, 3]); assert_eq!(reload.get("foo").unwrap(), &[1, 2, 3]);
} }
#[test] #[test]
fn query_basic() { fn query_basic() {
let temp_file = NamedTempFile::new().unwrap(); let temp_file = NamedTempFile::new().unwrap();
let datastore = JsonFileDatastore::<Vec<u8>>::new(temp_file.path()).unwrap(); let datastore = JsonFileDatastore::<Vec<u8>>::new(temp_file.path()).unwrap();
datastore.put("foo1".into(), vec![6, 7, 8]); datastore.put("foo1".into(), vec![6, 7, 8]);
datastore.put("foo2".into(), vec![6, 7, 8]); datastore.put("foo2".into(), vec![6, 7, 8]);
datastore.put("foo3".into(), vec![7, 8, 9]); datastore.put("foo3".into(), vec![7, 8, 9]);
datastore.put("foo4".into(), vec![10, 11, 12]); datastore.put("foo4".into(), vec![10, 11, 12]);
datastore.put("foo5".into(), vec![13, 14, 15]); datastore.put("foo5".into(), vec![13, 14, 15]);
datastore.put("bar1".into(), vec![0, 255, 127]); datastore.put("bar1".into(), vec![0, 255, 127]);
datastore.flush().unwrap(); datastore.flush().unwrap();
let query = datastore.query(Query { let query = datastore
prefix: "fo".into(), .query(Query {
filters: vec![ prefix: "fo".into(),
Filter { filters: vec![
ty: FilterTy::ValueCompare(&vec![6, 7, 8].into()), Filter {
operation: FilterOp::NotEqual, ty: FilterTy::ValueCompare(&vec![6, 7, 8].into()),
}, operation: FilterOp::NotEqual,
], },
orders: vec![Order::ByKeyDesc], ],
skip: 1, orders: vec![Order::ByKeyDesc],
limit: u64::max_value(), skip: 1,
keys_only: false, limit: u64::max_value(),
}) keys_only: false,
.collect() })
.wait() .collect()
.unwrap(); .wait()
.unwrap();
assert_eq!(query[0].0, "foo4"); assert_eq!(query[0].0, "foo4");
assert_eq!(query[0].1, &[10, 11, 12]); assert_eq!(query[0].1, &[10, 11, 12]);
assert_eq!(query[1].0, "foo3"); assert_eq!(query[1].0, "foo3");
assert_eq!(query[1].1, &[7, 8, 9]); assert_eq!(query[1].1, &[7, 8, 9]);
} }
} }

View File

@ -23,78 +23,78 @@
//! General-purpose key-value storage. //! General-purpose key-value storage.
//! The keys are strings, and the values are of any type you want. //! The keys are strings, and the values are of any type you want.
//! //!
//! > **Note**: This crate is meant to be a utility for the implementation of other crates ; it //! > **Note**: This crate is meant to be a utility for the implementation of other crates ; it
//! > does not directly participate in the stack of libp2p. //! > does not directly participate in the stack of libp2p.
//! //!
//! This crate provides the `Datastore` trait, whose template parameter is the type of the value. //! This crate provides the `Datastore` trait, whose template parameter is the type of the value.
//! It is implemented on types that represent a key-value storage. //! It is implemented on types that represent a key-value storage.
//! The only available implementation for now is `JsonFileDatastore`. //! The only available implementation for now is `JsonFileDatastore`.
//! //!
//! # JSON file datastore //! # JSON file datastore
//! //!
//! The `JsonFileDatastore` can provide a key-value storage that loads and stores data in a single //! The `JsonFileDatastore` can provide a key-value storage that loads and stores data in a single
//! JSON file. It is only available if the value implements the `Serialize`, `DeserializeOwned` //! JSON file. It is only available if the value implements the `Serialize`, `DeserializeOwned`
//! and `Clone` traits. //! and `Clone` traits.
//! //!
//! The `JsonFileDatastore::new` method will attempt to load existing data from the path you pass //! The `JsonFileDatastore::new` method will attempt to load existing data from the path you pass
//! as parameter. This path is also where the data will be stored. The content of the store is //! as parameter. This path is also where the data will be stored. The content of the store is
//! flushed on drop or if you call `flush()`. //! flushed on drop or if you call `flush()`.
//! //!
//! ```no_run //! ```no_run
//! use datastore::Datastore; //! use datastore::Datastore;
//! use datastore::JsonFileDatastore; //! use datastore::JsonFileDatastore;
//! //!
//! let datastore = JsonFileDatastore::<Vec<u8>>::new("/tmp/test.json").unwrap(); //! let datastore = JsonFileDatastore::<Vec<u8>>::new("/tmp/test.json").unwrap();
//! datastore.put("foo".into(), vec![1, 2, 3]); //! datastore.put("foo".into(), vec![1, 2, 3]);
//! datastore.put("bar".into(), vec![0, 255, 127]); //! datastore.put("bar".into(), vec![0, 255, 127]);
//! assert_eq!(datastore.get("foo").unwrap(), &[1, 2, 3]); //! assert_eq!(datastore.get("foo").unwrap(), &[1, 2, 3]);
//! datastore.flush().unwrap(); // optional //! datastore.flush().unwrap(); // optional
//! ``` //! ```
//! //!
//! # Query //! # Query
//! //!
//! In addition to simple operations such as `get` or `put`, the `Datastore` trait also provides //! In addition to simple operations such as `get` or `put`, the `Datastore` trait also provides
//! a way to perform queries on the key-value storage, using the `query` method. //! a way to perform queries on the key-value storage, using the `query` method.
//! //!
//! The struct returned by the `query` method implements the `Stream` trait from `futures`, //! The struct returned by the `query` method implements the `Stream` trait from `futures`,
//! meaning that the result is asynchronous. //! meaning that the result is asynchronous.
//! //!
//! > **Note**: For now the API of the `get` and `has` methods makes them potentially blocking //! > **Note**: For now the API of the `get` and `has` methods makes them potentially blocking
//! > operations, though the only available implementation doesn't block. The API of these //! > operations, though the only available implementation doesn't block. The API of these
//! > methods may become asynchronous in the future if deemed necessary. //! > methods may become asynchronous in the future if deemed necessary.
//! //!
//! ```no_run //! ```no_run
//! extern crate datastore; //! extern crate datastore;
//! extern crate futures; //! extern crate futures;
//! //!
//! # fn main() { //! # fn main() {
//! use datastore::{Query, Order, Filter, FilterTy, FilterOp}; //! use datastore::{Query, Order, Filter, FilterTy, FilterOp};
//! use datastore::Datastore; //! use datastore::Datastore;
//! use datastore::JsonFileDatastore; //! use datastore::JsonFileDatastore;
//! use futures::{Future, Stream}; //! use futures::{Future, Stream};
//! //!
//! let datastore = JsonFileDatastore::<Vec<u8>>::new("/tmp/test.json").unwrap(); //! let datastore = JsonFileDatastore::<Vec<u8>>::new("/tmp/test.json").unwrap();
//! let query = datastore.query(Query { //! let query = datastore.query(Query {
//! // Only return the keys that start with this prefix. //! // Only return the keys that start with this prefix.
//! prefix: "fo".into(), //! prefix: "fo".into(),
//! // List of filters for the keys and/or values. //! // List of filters for the keys and/or values.
//! filters: vec![ //! filters: vec![
//! Filter { //! Filter {
//! ty: FilterTy::ValueCompare(&vec![6, 7, 8].into()), //! ty: FilterTy::ValueCompare(&vec![6, 7, 8].into()),
//! operation: FilterOp::NotEqual, //! operation: FilterOp::NotEqual,
//! }, //! },
//! ], //! ],
//! // Order in which to sort the results. //! // Order in which to sort the results.
//! orders: vec![Order::ByKeyDesc], //! orders: vec![Order::ByKeyDesc],
//! // Number of entries to skip at the beginning of the results (after sorting). //! // Number of entries to skip at the beginning of the results (after sorting).
//! skip: 1, //! skip: 1,
//! // Limit to the number of entries to return (use `u64::max_value()` for no limit). //! // Limit to the number of entries to return (use `u64::max_value()` for no limit).
//! limit: 12, //! limit: 12,
//! // If true, don't load the values. For optimization purposes. //! // If true, don't load the values. For optimization purposes.
//! keys_only: false, //! keys_only: false,
//! }); //! });
//! //!
//! let results = query.collect().wait().unwrap(); //! let results = query.collect().wait().unwrap();
//! println!("{:?}", results); //! println!("{:?}", results);
//! # } //! # }
@ -117,61 +117,64 @@ mod query;
mod json_file; mod json_file;
pub use self::json_file::{JsonFileDatastore, JsonFileDatastoreEntry}; pub use self::json_file::{JsonFileDatastore, JsonFileDatastoreEntry};
pub use self::query::{Query, Order, Filter, FilterTy, FilterOp}; pub use self::query::{Filter, FilterOp, FilterTy, Order, Query};
/// Abstraction over any struct that can store `(key, value)` pairs. /// Abstraction over any struct that can store `(key, value)` pairs.
pub trait Datastore<T> { pub trait Datastore<T> {
/// Locked entry. /// Locked entry.
type Entry: DerefMut<Target = T>; type Entry: DerefMut<Target = T>;
/// Output of a query. /// Output of a query.
type QueryResult: Stream<Item = (String, T), Error = IoError>; type QueryResult: Stream<Item = (String, T), Error = IoError>;
/// Sets the value of a key. /// Sets the value of a key.
#[inline] #[inline]
fn put(self, key: Cow<str>, value: T) fn put(self, key: Cow<str>, value: T)
where Self: Sized where
{ Self: Sized,
*self.lock_or_create(key) = value; {
} *self.lock_or_create(key) = value;
}
/// Checks if an entry exists, and if so locks it. /// Checks if an entry exists, and if so locks it.
/// ///
/// Trying to lock a value that is already locked will block, therefore you should keep locks /// Trying to lock a value that is already locked will block, therefore you should keep locks
/// for a duration that is as short as possible. /// for a duration that is as short as possible.
fn lock(self, key: Cow<str>) -> Option<Self::Entry>; fn lock(self, key: Cow<str>) -> Option<Self::Entry>;
/// Locks an entry if it exists, or creates it otherwise. /// Locks an entry if it exists, or creates it otherwise.
/// ///
/// Same as `put` followed with `lock`, except that it is atomic. /// Same as `put` followed with `lock`, except that it is atomic.
fn lock_or_create(self, key: Cow<str>) -> Self::Entry; fn lock_or_create(self, key: Cow<str>) -> Self::Entry;
/// Returns the value corresponding to this key by cloning it. /// Returns the value corresponding to this key by cloning it.
#[inline] #[inline]
fn get(self, key: &str) -> Option<T> fn get(self, key: &str) -> Option<T>
where Self: Sized, where
T: Clone Self: Sized,
{ T: Clone,
self.lock(key.into()).map(|v| v.clone()) {
} self.lock(key.into()).map(|v| v.clone())
}
/// Returns true if the datastore contains the given key. /// Returns true if the datastore contains the given key.
/// ///
/// > **Note**: Keep in mind that using this operation is probably racy. A secondary thread /// > **Note**: Keep in mind that using this operation is probably racy. A secondary thread
/// > can delete a key right after you called `has()`. In other words, this function /// > can delete a key right after you called `has()`. In other words, this function
/// > returns whether an entry with that key existed in the short past. /// > returns whether an entry with that key existed in the short past.
#[inline] #[inline]
fn has(self, key: &str) -> bool fn has(self, key: &str) -> bool
where Self: Sized where
{ Self: Sized,
self.lock(key.into()).is_some() {
} self.lock(key.into()).is_some()
}
/// Removes the given key from the datastore. Returns the old value if the key existed. /// Removes the given key from the datastore. Returns the old value if the key existed.
fn delete(self, key: &str) -> Option<T>; fn delete(self, key: &str) -> Option<T>;
/// Executes a query on the key-value store. /// Executes a query on the key-value store.
/// ///
/// This operation is expensive on some implementations and cheap on others. It is your /// This operation is expensive on some implementations and cheap on others. It is your
/// responsibility to pick the right implementation for the right job. /// responsibility to pick the right implementation for the right job.
fn query(self, query: Query<T>) -> Self::QueryResult; fn query(self, query: Query<T>) -> Self::QueryResult;
} }

View File

@ -18,8 +18,8 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
use futures::{Stream, Future, Async, Poll}; use futures::{Async, Future, Poll, Stream};
use futures::stream::{iter_ok, Take as StreamTake, Skip as StreamSkip}; use futures::stream::{iter_ok, Skip as StreamSkip, Take as StreamTake};
use std::borrow::Cow; use std::borrow::Cow;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::io::Error as IoError; use std::io::Error as IoError;
@ -32,289 +32,319 @@ use std::vec::IntoIter as VecIntoIter;
/// filters, orders, skip, limit). /// filters, orders, skip, limit).
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Query<'a, T: 'a> { pub struct Query<'a, T: 'a> {
/// Only the keys that start with `prefix` will be returned. /// Only the keys that start with `prefix` will be returned.
pub prefix: Cow<'a, str>, pub prefix: Cow<'a, str>,
/// Filters to apply on the results. /// Filters to apply on the results.
pub filters: Vec<Filter<'a, T>>, pub filters: Vec<Filter<'a, T>>,
/// How to order the keys. Applied sequentially. /// How to order the keys. Applied sequentially.
pub orders: Vec<Order>, pub orders: Vec<Order>,
/// Number of elements to skip from at the start of the results. /// Number of elements to skip from at the start of the results.
pub skip: u64, pub skip: u64,
/// Maximum number of elements in the results. /// Maximum number of elements in the results.
pub limit: u64, pub limit: u64,
/// Only return keys. If true, then all the `Vec`s of the data will be empty. /// Only return keys. If true, then all the `Vec`s of the data will be empty.
pub keys_only: bool, pub keys_only: bool,
} }
/// A filter to apply to the results set. /// A filter to apply to the results set.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Filter<'a, T: 'a> { pub struct Filter<'a, T: 'a> {
/// Type of filter and value to compare with. /// Type of filter and value to compare with.
pub ty: FilterTy<'a, T>, pub ty: FilterTy<'a, T>,
/// Comparison operation. /// Comparison operation.
pub operation: FilterOp, pub operation: FilterOp,
} }
/// Type of filter and value to compare with. /// Type of filter and value to compare with.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum FilterTy<'a, T: 'a> { pub enum FilterTy<'a, T: 'a> {
/// Compare the key with a reference value. /// Compare the key with a reference value.
KeyCompare(Cow<'a, str>), KeyCompare(Cow<'a, str>),
/// Compare the value with a reference value. /// Compare the value with a reference value.
ValueCompare(&'a T), ValueCompare(&'a T),
} }
/// Filtering operation. /// Filtering operation.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum FilterOp { pub enum FilterOp {
Equal, Equal,
NotEqual, NotEqual,
Less, Less,
LessOrEqual, LessOrEqual,
Greater, Greater,
GreaterOrEqual, GreaterOrEqual,
} }
/// Order in which to sort the results of a query. /// Order in which to sort the results of a query.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum Order { pub enum Order {
/// Put the values in ascending order. /// Put the values in ascending order.
ByValueAsc, ByValueAsc,
/// Put the values in descending order. /// Put the values in descending order.
ByValueDesc, ByValueDesc,
/// Put the keys in ascending order. /// Put the keys in ascending order.
ByKeyAsc, ByKeyAsc,
/// Put the keys in descending order. /// Put the keys in descending order.
ByKeyDesc, ByKeyDesc,
} }
/// Naively applies a query on a set of results. /// Naively applies a query on a set of results.
pub fn naive_apply_query<'a, S, V>(stream: S, query: Query<'a, V>) pub fn naive_apply_query<'a, S, V>(
-> StreamTake<StreamSkip<NaiveKeysOnlyApply<NaiveApplyOrdered<NaiveFiltersApply<'a, NaivePrefixApply<'a, S>, VecIntoIter<Filter<'a, V>>>, V>>>> stream: S,
where S: Stream<Item = (String, V), Error = IoError> + 'a, query: Query<'a, V>,
V: Clone + PartialOrd + Default + 'static ) -> StreamTake<
StreamSkip<
NaiveKeysOnlyApply<
NaiveApplyOrdered<
NaiveFiltersApply<'a, NaivePrefixApply<'a, S>, VecIntoIter<Filter<'a, V>>>,
V,
>,
>,
>,
>
where
S: Stream<Item = (String, V), Error = IoError> + 'a,
V: Clone + PartialOrd + Default + 'static,
{ {
let prefixed = naive_apply_prefix(stream, query.prefix); let prefixed = naive_apply_prefix(stream, query.prefix);
let filtered = naive_apply_filters(prefixed, query.filters.into_iter()); let filtered = naive_apply_filters(prefixed, query.filters.into_iter());
let ordered = naive_apply_ordered(filtered, query.orders); let ordered = naive_apply_ordered(filtered, query.orders);
let keys_only = naive_apply_keys_only(ordered, query.keys_only); let keys_only = naive_apply_keys_only(ordered, query.keys_only);
naive_apply_skip_limit(keys_only, query.skip, query.limit) naive_apply_skip_limit(keys_only, query.skip, query.limit)
} }
/// Skips the `skip` first element of a stream and only returns `limit` elements. /// Skips the `skip` first element of a stream and only returns `limit` elements.
#[inline] #[inline]
pub fn naive_apply_skip_limit<S, T>(stream: S, skip: u64, limit: u64) -> StreamTake<StreamSkip<S>> pub fn naive_apply_skip_limit<S, T>(stream: S, skip: u64, limit: u64) -> StreamTake<StreamSkip<S>>
where S: Stream<Item = (String, T), Error = IoError> where
S: Stream<Item = (String, T), Error = IoError>,
{ {
stream.skip(skip).take(limit) stream.skip(skip).take(limit)
} }
/// Filters the result of a stream to empty values if `keys_only` is true. /// Filters the result of a stream to empty values if `keys_only` is true.
#[inline] #[inline]
pub fn naive_apply_keys_only<S, T>(stream: S, keys_only: bool) -> NaiveKeysOnlyApply<S> pub fn naive_apply_keys_only<S, T>(stream: S, keys_only: bool) -> NaiveKeysOnlyApply<S>
where S: Stream<Item = (String, T), Error = IoError> where
S: Stream<Item = (String, T), Error = IoError>,
{ {
NaiveKeysOnlyApply { NaiveKeysOnlyApply {
keys_only: keys_only, keys_only: keys_only,
stream: stream, stream: stream,
} }
} }
/// Returned by `naive_apply_keys_only`. /// Returned by `naive_apply_keys_only`.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NaiveKeysOnlyApply<S> { pub struct NaiveKeysOnlyApply<S> {
keys_only: bool, keys_only: bool,
stream: S, stream: S,
} }
impl<S, T> Stream for NaiveKeysOnlyApply<S> impl<S, T> Stream for NaiveKeysOnlyApply<S>
where S: Stream<Item = (String, T), Error = IoError>, where
T: Default S: Stream<Item = (String, T), Error = IoError>,
T: Default,
{ {
type Item = (String, T); type Item = (String, T);
type Error = IoError; type Error = IoError;
#[inline] #[inline]
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if self.keys_only { if self.keys_only {
Ok(Async::Ready(try_ready!(self.stream.poll()).map(|mut v| { Ok(Async::Ready(try_ready!(self.stream.poll()).map(|mut v| {
v.1 = Default::default(); v.1 = Default::default();
v v
}))) })))
} else { } else {
self.stream.poll() self.stream.poll()
} }
} }
} }
/// Filters the result of a stream to only keep the results with a prefix. /// Filters the result of a stream to only keep the results with a prefix.
#[inline] #[inline]
pub fn naive_apply_prefix<'a, S, T>(stream: S, prefix: Cow<'a, str>) -> NaivePrefixApply<'a, S> pub fn naive_apply_prefix<'a, S, T>(stream: S, prefix: Cow<'a, str>) -> NaivePrefixApply<'a, S>
where S: Stream<Item = (String, T), Error = IoError> where
S: Stream<Item = (String, T), Error = IoError>,
{ {
NaivePrefixApply { prefix: prefix, stream: stream } NaivePrefixApply {
prefix: prefix,
stream: stream,
}
} }
/// Returned by `naive_apply_prefix`. /// Returned by `naive_apply_prefix`.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NaivePrefixApply<'a, S> { pub struct NaivePrefixApply<'a, S> {
prefix: Cow<'a, str>, prefix: Cow<'a, str>,
stream: S, stream: S,
} }
impl<'a, S, T> Stream for NaivePrefixApply<'a, S> impl<'a, S, T> Stream for NaivePrefixApply<'a, S>
where S: Stream<Item = (String, T), Error = IoError> where
S: Stream<Item = (String, T), Error = IoError>,
{ {
type Item = (String, T); type Item = (String, T);
type Error = IoError; type Error = IoError;
#[inline] #[inline]
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
loop { loop {
let item = try_ready!(self.stream.poll()); let item = try_ready!(self.stream.poll());
match item { match item {
Some(i) => { Some(i) => {
if i.0.starts_with(&*self.prefix) { if i.0.starts_with(&*self.prefix) {
return Ok(Async::Ready(Some(i))); return Ok(Async::Ready(Some(i)));
} }
} }
None => return Ok(Async::Ready(None)), None => return Ok(Async::Ready(None)),
} }
} }
} }
} }
/// Applies orderings on the stream data. Will simply pass data through if the list of orderings /// Applies orderings on the stream data. Will simply pass data through if the list of orderings
/// is empty. Otherwise will need to collect. /// is empty. Otherwise will need to collect.
pub fn naive_apply_ordered<'a, S, I, V>(stream: S, orders_iter: I) -> NaiveApplyOrdered<'a, S, V> pub fn naive_apply_ordered<'a, S, I, V>(stream: S, orders_iter: I) -> NaiveApplyOrdered<'a, S, V>
where S: Stream<Item = (String, V), Error = IoError> + 'a, where
I: IntoIterator<Item = Order>, S: Stream<Item = (String, V), Error = IoError> + 'a,
I::IntoIter: 'a, I: IntoIterator<Item = Order>,
V: PartialOrd + 'static I::IntoIter: 'a,
V: PartialOrd + 'static,
{ {
let orders_iter = orders_iter.into_iter(); let orders_iter = orders_iter.into_iter();
if orders_iter.size_hint().1 == Some(0) { if orders_iter.size_hint().1 == Some(0) {
return NaiveApplyOrdered { inner: NaiveApplyOrderedInner::PassThrough(stream) }; return NaiveApplyOrdered {
} inner: NaiveApplyOrderedInner::PassThrough(stream),
};
}
let collected = stream.collect() let collected = stream
.and_then(move |mut collected| { .collect()
for order in orders_iter { .and_then(move |mut collected| {
match order { for order in orders_iter {
Order::ByValueAsc => { match order {
collected.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal)); Order::ByValueAsc => {
} collected.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal));
Order::ByValueDesc => { }
collected.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal)); Order::ByValueDesc => {
} collected.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
Order::ByKeyAsc => { }
collected.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); Order::ByKeyAsc => {
} collected.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal));
Order::ByKeyDesc => { }
collected.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal)); Order::ByKeyDesc => {
} collected.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));
} }
} }
Ok(iter_ok(collected.into_iter())) }
}) Ok(iter_ok(collected.into_iter()))
.flatten_stream(); })
.flatten_stream();
NaiveApplyOrdered { inner: NaiveApplyOrderedInner::Collected(Box::new(collected)) } NaiveApplyOrdered {
inner: NaiveApplyOrderedInner::Collected(Box::new(collected)),
}
} }
/// Returned by `naive_apply_ordered`. /// Returned by `naive_apply_ordered`.
pub struct NaiveApplyOrdered<'a, S, T> { pub struct NaiveApplyOrdered<'a, S, T> {
inner: NaiveApplyOrderedInner<'a, S, T>, inner: NaiveApplyOrderedInner<'a, S, T>,
} }
enum NaiveApplyOrderedInner<'a, S, T> { enum NaiveApplyOrderedInner<'a, S, T> {
PassThrough(S), PassThrough(S),
Collected(Box<Stream<Item = (String, T), Error = IoError> + 'a>), Collected(Box<Stream<Item = (String, T), Error = IoError> + 'a>),
} }
impl<'a, S, V> Stream for NaiveApplyOrdered<'a, S, V> impl<'a, S, V> Stream for NaiveApplyOrdered<'a, S, V>
where S: Stream<Item = (String, V), Error = IoError> where
S: Stream<Item = (String, V), Error = IoError>,
{ {
type Item = (String, V); type Item = (String, V);
type Error = IoError; type Error = IoError;
#[inline] #[inline]
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
match self.inner { match self.inner {
NaiveApplyOrderedInner::PassThrough(ref mut s) => s.poll(), NaiveApplyOrderedInner::PassThrough(ref mut s) => s.poll(),
NaiveApplyOrderedInner::Collected(ref mut s) => s.poll(), NaiveApplyOrderedInner::Collected(ref mut s) => s.poll(),
} }
} }
} }
/// Filters the result of a stream to apply a set of filters. /// Filters the result of a stream to apply a set of filters.
#[inline] #[inline]
pub fn naive_apply_filters<'a, S, I, V>(stream: S, filters: I) -> NaiveFiltersApply<'a, S, I> pub fn naive_apply_filters<'a, S, I, V>(stream: S, filters: I) -> NaiveFiltersApply<'a, S, I>
where S: Stream<Item = (String, V), Error = IoError>, where
I: Iterator<Item = Filter<'a, V>> + Clone, S: Stream<Item = (String, V), Error = IoError>,
V: 'a I: Iterator<Item = Filter<'a, V>> + Clone,
V: 'a,
{ {
NaiveFiltersApply { NaiveFiltersApply {
filters: filters, filters: filters,
stream: stream, stream: stream,
marker: PhantomData, marker: PhantomData,
} }
} }
/// Returned by `naive_apply_prefix`. /// Returned by `naive_apply_prefix`.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NaiveFiltersApply<'a, S, I> { pub struct NaiveFiltersApply<'a, S, I> {
filters: I, filters: I,
stream: S, stream: S,
marker: PhantomData<&'a ()>, marker: PhantomData<&'a ()>,
} }
impl<'a, S, I, T> Stream for NaiveFiltersApply<'a, S, I> impl<'a, S, I, T> Stream for NaiveFiltersApply<'a, S, I>
where S: Stream<Item = (String, T), Error = IoError>, where
I: Iterator<Item = Filter<'a, T>> + Clone, S: Stream<Item = (String, T), Error = IoError>,
T: PartialOrd + 'a I: Iterator<Item = Filter<'a, T>> + Clone,
T: PartialOrd + 'a,
{ {
type Item = (String, T); type Item = (String, T);
type Error = IoError; type Error = IoError;
#[inline] #[inline]
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
'outer: loop { 'outer: loop {
let item = try_ready!(self.stream.poll()); let item = try_ready!(self.stream.poll());
match item { match item {
Some(i) => { Some(i) => {
for filter in self.filters.clone() { for filter in self.filters.clone() {
if !naive_filter_test(&i, &filter) { if !naive_filter_test(&i, &filter) {
continue 'outer; continue 'outer;
} }
} }
return Ok(Async::Ready(Some(i))); return Ok(Async::Ready(Some(i)));
} }
None => return Ok(Async::Ready(None)), None => return Ok(Async::Ready(None)),
} }
} }
} }
} }
#[inline] #[inline]
fn naive_filter_test<T>(entry: &(String, T), filter: &Filter<T>) -> bool fn naive_filter_test<T>(entry: &(String, T), filter: &Filter<T>) -> bool
where T: PartialOrd where
T: PartialOrd,
{ {
let (expected_ordering, revert_expected) = match filter.operation { let (expected_ordering, revert_expected) = match filter.operation {
FilterOp::Equal => (Ordering::Equal, false), FilterOp::Equal => (Ordering::Equal, false),
FilterOp::NotEqual => (Ordering::Equal, true), FilterOp::NotEqual => (Ordering::Equal, true),
FilterOp::Less => (Ordering::Less, false), FilterOp::Less => (Ordering::Less, false),
FilterOp::GreaterOrEqual => (Ordering::Less, true), FilterOp::GreaterOrEqual => (Ordering::Less, true),
FilterOp::Greater => (Ordering::Greater, false), FilterOp::Greater => (Ordering::Greater, false),
FilterOp::LessOrEqual => (Ordering::Greater, true), FilterOp::LessOrEqual => (Ordering::Greater, true),
}; };
match filter.ty { match filter.ty {
FilterTy::KeyCompare(ref ref_value) => { FilterTy::KeyCompare(ref ref_value) => {
((&*entry.0).cmp(&**ref_value) == expected_ordering) != revert_expected ((&*entry.0).cmp(&**ref_value) == expected_ordering) != revert_expected
} }
FilterTy::ValueCompare(ref ref_value) => { FilterTy::ValueCompare(ref ref_value) => {
(entry.1.partial_cmp(&**ref_value) == Some(expected_ordering)) != revert_expected (entry.1.partial_cmp(&**ref_value) == Some(expected_ordering)) != revert_expected
} }
} }
} }

View File

@ -31,7 +31,7 @@ extern crate tokio_io;
use futures::{Future, Sink, Stream}; use futures::{Future, Sink, Stream};
use futures::sync::oneshot; use futures::sync::oneshot;
use std::env; use std::env;
use swarm::{UpgradeExt, SimpleProtocol, Transport, DeniedConnectionUpgrade}; use swarm::{DeniedConnectionUpgrade, SimpleProtocol, Transport, UpgradeExt};
use tcp::TcpConfig; use tcp::TcpConfig;
use tokio_core::reactor::Core; use tokio_core::reactor::Core;
use tokio_io::AsyncRead; use tokio_io::AsyncRead;
@ -40,7 +40,9 @@ use websocket::WsConfig;
fn main() { fn main() {
// Determine which address to dial. // Determine which address to dial.
let target_addr = env::args().nth(1).unwrap_or("/ip4/127.0.0.1/tcp/10333".to_owned()); let target_addr = env::args()
.nth(1)
.unwrap_or("/ip4/127.0.0.1/tcp/10333".to_owned());
// We start by building the tokio engine that will run all the sockets. // We start by building the tokio engine that will run all the sockets.
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
@ -82,10 +84,13 @@ fn main() {
// connections for us. The second parameter we pass is the connection upgrade that is accepted // connections for us. The second parameter we pass is the connection upgrade that is accepted
// by the listening part. We don't want to accept anything, so we pass a dummy object that // by the listening part. We don't want to accept anything, so we pass a dummy object that
// represents a connection that is always denied. // represents a connection that is always denied.
let (swarm_controller, swarm_future) = swarm::swarm(transport, DeniedConnectionUpgrade, let (swarm_controller, swarm_future) = swarm::swarm(
transport,
DeniedConnectionUpgrade,
|_socket, _client_addr| -> Result<(), _> { |_socket, _client_addr| -> Result<(), _> {
unreachable!("All incoming connections should have been denied") unreachable!("All incoming connections should have been denied")
}); },
);
// Building a struct that represents the protocol that we are going to use for dialing. // Building a struct that represents the protocol that we are going to use for dialing.
let proto = SimpleProtocol::new("/echo/1.0.0", |socket| { let proto = SimpleProtocol::new("/echo/1.0.0", |socket| {

View File

@ -1,21 +1,21 @@
// Copyright 2017 Parity Technologies (UK) Ltd. // Copyright 2017 Parity Technologies (UK) Ltd.
// //
// Permission is hereby granted, free of charge, to any person obtaining a // Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"), // copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation // to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, // the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the // and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions: // Software is furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
extern crate bytes; extern crate bytes;
@ -28,10 +28,10 @@ extern crate multiplex;
extern crate tokio_core; extern crate tokio_core;
extern crate tokio_io; extern crate tokio_io;
use futures::future::{Future, IntoFuture, loop_fn, Loop}; use futures::future::{loop_fn, Future, IntoFuture, Loop};
use futures::{Stream, Sink}; use futures::{Sink, Stream};
use std::env; use std::env;
use swarm::{Transport, UpgradeExt, SimpleProtocol}; use swarm::{SimpleProtocol, Transport, UpgradeExt};
use tcp::TcpConfig; use tcp::TcpConfig;
use tokio_core::reactor::Core; use tokio_core::reactor::Core;
use tokio_io::AsyncRead; use tokio_io::AsyncRead;
@ -40,7 +40,9 @@ use websocket::WsConfig;
fn main() { fn main() {
// Determine which address to listen to. // Determine which address to listen to.
let listen_addr = env::args().nth(1).unwrap_or("/ip4/0.0.0.0/tcp/10333".to_owned()); let listen_addr = env::args()
.nth(1)
.unwrap_or("/ip4/0.0.0.0/tcp/10333".to_owned());
// We start by building the tokio engine that will run all the sockets. // We start by building the tokio engine that will run all the sockets.
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
@ -107,14 +109,16 @@ fn main() {
.and_then(move |(msg, rest)| { .and_then(move |(msg, rest)| {
if let Some(msg) = msg { if let Some(msg) = msg {
// One message has been received. We send it back to the client. // One message has been received. We send it back to the client.
println!("Received a message from {}: {:?}\n => Sending back \ println!(
identical message to remote", client_addr, msg); "Received a message from {}: {:?}\n => Sending back \
identical message to remote",
client_addr, msg
);
Box::new(rest.send(msg.freeze()).map(|m| Loop::Continue(m))) Box::new(rest.send(msg.freeze()).map(|m| Loop::Continue(m)))
as Box<Future<Item = _, Error = _>> as Box<Future<Item = _, Error = _>>
} else { } else {
// End of stream. Connection closed. Breaking the loop. // End of stream. Connection closed. Breaking the loop.
println!("Received EOF from {}\n => Dropping connection", println!("Received EOF from {}\n => Dropping connection", client_addr);
client_addr);
Box::new(Ok(Loop::Break(())).into_future()) Box::new(Ok(Loop::Break(())).into_future())
as Box<Future<Item = _, Error = _>> as Box<Future<Item = _, Error = _>>
} }

View File

@ -31,13 +31,15 @@ extern crate tokio_io;
use futures::Future; use futures::Future;
use futures::sync::oneshot; use futures::sync::oneshot;
use std::env; use std::env;
use swarm::{UpgradeExt, Transport, DeniedConnectionUpgrade}; use swarm::{DeniedConnectionUpgrade, Transport, UpgradeExt};
use tcp::TcpConfig; use tcp::TcpConfig;
use tokio_core::reactor::Core; use tokio_core::reactor::Core;
fn main() { fn main() {
// Determine which address to dial. // Determine which address to dial.
let target_addr = env::args().nth(1).unwrap_or("/ip4/127.0.0.1/tcp/4001".to_owned()); let target_addr = env::args()
.nth(1)
.unwrap_or("/ip4/127.0.0.1/tcp/4001".to_owned());
// We start by building the tokio engine that will run all the sockets. // We start by building the tokio engine that will run all the sockets.
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
@ -74,10 +76,13 @@ fn main() {
// connections for us. The second parameter we pass is the connection upgrade that is accepted // connections for us. The second parameter we pass is the connection upgrade that is accepted
// by the listening part. We don't want to accept anything, so we pass a dummy object that // by the listening part. We don't want to accept anything, so we pass a dummy object that
// represents a connection that is always denied. // represents a connection that is always denied.
let (swarm_controller, swarm_future) = swarm::swarm(transport, DeniedConnectionUpgrade, let (swarm_controller, swarm_future) = swarm::swarm(
transport,
DeniedConnectionUpgrade,
|_socket, _client_addr| -> Result<(), _> { |_socket, _client_addr| -> Result<(), _> {
unreachable!("All incoming connections should have been denied") unreachable!("All incoming connections should have been denied")
}); },
);
// We now use the controller to dial to the address. // We now use the controller to dial to the address.
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
@ -101,5 +106,9 @@ fn main() {
// `swarm_future` is a future that contains all the behaviour that we want, but nothing has // `swarm_future` is a future that contains all the behaviour that we want, but nothing has
// actually started yet. Because we created the `TcpConfig` with tokio, we need to run the // actually started yet. Because we created the `TcpConfig` with tokio, we need to run the
// future through the tokio core. // future through the tokio core.
core.run(rx.select(swarm_future.map_err(|_| unreachable!())).map_err(|(e, _)| e).map(|_| ())).unwrap(); core.run(
rx.select(swarm_future.map_err(|_| unreachable!()))
.map_err(|(e, _)| e)
.map(|_| ()),
).unwrap();
} }

View File

@ -22,16 +22,16 @@ extern crate libp2p_peerstore;
extern crate libp2p_swarm; extern crate libp2p_swarm;
extern crate multiaddr; extern crate multiaddr;
use libp2p_peerstore::{PeerId, PeerAccess, Peerstore}; use libp2p_peerstore::{PeerAccess, PeerId, Peerstore};
use multiaddr::Multiaddr; use multiaddr::Multiaddr;
use std::time::Duration; use std::time::Duration;
/// Stores initial addresses on the given peer store. Uses a very large timeout. /// Stores initial addresses on the given peer store. Uses a very large timeout.
pub fn ipfs_bootstrap<P>(peer_store: P) pub fn ipfs_bootstrap<P>(peer_store: P)
where where
P: Peerstore + Clone, P: Peerstore + Clone,
{ {
const ADDRESSES: &[&str] = &[ const ADDRESSES: &[&str] = &[
"/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ",
"/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM", "/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM",
"/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm", "/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm",
@ -43,22 +43,22 @@ where
"/dns4/wss1.bootstrap.libp2p.io/tcp/443/wss/ipfs/Qmbut9Ywz9YEDrz8ySBSgWyJk41Uvm2QJPhwDJzJyGFsD6" "/dns4/wss1.bootstrap.libp2p.io/tcp/443/wss/ipfs/Qmbut9Ywz9YEDrz8ySBSgWyJk41Uvm2QJPhwDJzJyGFsD6"
]; ];
let ttl = Duration::from_secs(100 * 365 * 24 * 3600); let ttl = Duration::from_secs(100 * 365 * 24 * 3600);
for address in ADDRESSES.iter() { for address in ADDRESSES.iter() {
let mut multiaddr = address let mut multiaddr = address
.parse::<Multiaddr>() .parse::<Multiaddr>()
.expect("failed to parse hard-coded multiaddr"); .expect("failed to parse hard-coded multiaddr");
let ipfs_component = multiaddr.pop().expect("hard-coded multiaddr is empty"); let ipfs_component = multiaddr.pop().expect("hard-coded multiaddr is empty");
let public_key = match ipfs_component { let public_key = match ipfs_component {
multiaddr::AddrComponent::IPFS(key) => key, multiaddr::AddrComponent::IPFS(key) => key,
_ => panic!("hard-coded multiaddr didn't end with /ipfs/"), _ => panic!("hard-coded multiaddr didn't end with /ipfs/"),
}; };
peer_store peer_store
.clone() .clone()
.peer_or_create(&PeerId::from_bytes(public_key).unwrap()) .peer_or_create(&PeerId::from_bytes(public_key).unwrap())
.add_addr(multiaddr, ttl.clone()); .add_addr(multiaddr, ttl.clone());
} }
} }

View File

@ -62,221 +62,221 @@ use tokio_dns::{CpuPoolResolver, Resolver};
/// Listening is unaffected. /// Listening is unaffected.
#[derive(Clone)] #[derive(Clone)]
pub struct DnsConfig<T> { pub struct DnsConfig<T> {
inner: T, inner: T,
resolver: CpuPoolResolver, resolver: CpuPoolResolver,
} }
impl<T> DnsConfig<T> { impl<T> DnsConfig<T> {
/// Creates a new configuration object for DNS. /// Creates a new configuration object for DNS.
#[inline] #[inline]
pub fn new(inner: T) -> DnsConfig<T> { pub fn new(inner: T) -> DnsConfig<T> {
DnsConfig::with_resolve_threads(inner, 1) DnsConfig::with_resolve_threads(inner, 1)
} }
/// Same as `new`, but allows specifying a number of threads for the resolving. /// Same as `new`, but allows specifying a number of threads for the resolving.
#[inline] #[inline]
pub fn with_resolve_threads(inner: T, num_threads: usize) -> DnsConfig<T> { pub fn with_resolve_threads(inner: T, num_threads: usize) -> DnsConfig<T> {
trace!(target: "libp2p-dns", "Created a CpuPoolResolver"); trace!(target: "libp2p-dns", "Created a CpuPoolResolver");
DnsConfig { DnsConfig {
inner, inner,
resolver: CpuPoolResolver::new(num_threads), resolver: CpuPoolResolver::new(num_threads),
} }
} }
} }
impl<T> fmt::Debug for DnsConfig<T> impl<T> fmt::Debug for DnsConfig<T>
where where
T: fmt::Debug, T: fmt::Debug,
{ {
#[inline] #[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_tuple("DnsConfig").field(&self.inner).finish() fmt.debug_tuple("DnsConfig").field(&self.inner).finish()
} }
} }
impl<T> Transport for DnsConfig<T> impl<T> Transport for DnsConfig<T>
where where
T: Transport + 'static, // TODO: 'static :-/ T: Transport + 'static, // TODO: 'static :-/
{ {
type RawConn = T::RawConn; type RawConn = T::RawConn;
type Listener = T::Listener; type Listener = T::Listener;
type ListenerUpgrade = T::ListenerUpgrade; type ListenerUpgrade = T::ListenerUpgrade;
type Dial = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>; type Dial = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>;
#[inline] #[inline]
fn listen_on(self, addr: Multiaddr) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> { fn listen_on(self, addr: Multiaddr) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> {
match self.inner.listen_on(addr) { match self.inner.listen_on(addr) {
Ok(r) => Ok(r), Ok(r) => Ok(r),
Err((inner, addr)) => Err(( Err((inner, addr)) => Err((
DnsConfig { DnsConfig {
inner, inner,
resolver: self.resolver, resolver: self.resolver,
}, },
addr, addr,
)), )),
} }
} }
fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> { fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> {
let contains_dns = addr.iter().any(|cmp| match cmp { let contains_dns = addr.iter().any(|cmp| match cmp {
AddrComponent::DNS4(_) => true, AddrComponent::DNS4(_) => true,
AddrComponent::DNS6(_) => true, AddrComponent::DNS6(_) => true,
_ => false, _ => false,
}); });
if !contains_dns { if !contains_dns {
trace!(target: "libp2p-dns", "Pass-through address without DNS: {}", addr); trace!(target: "libp2p-dns", "Pass-through address without DNS: {}", addr);
return match self.inner.dial(addr) { return match self.inner.dial(addr) {
Ok(d) => Ok(Box::new(d.into_future()) as Box<_>), Ok(d) => Ok(Box::new(d.into_future()) as Box<_>),
Err((inner, addr)) => Err(( Err((inner, addr)) => Err((
DnsConfig { DnsConfig {
inner, inner,
resolver: self.resolver, resolver: self.resolver,
}, },
addr, addr,
)), )),
}; };
} }
let resolver = self.resolver; let resolver = self.resolver;
trace!(target: "libp2p-dns", "Dialing address with DNS: {}", addr); trace!(target: "libp2p-dns", "Dialing address with DNS: {}", addr);
let resolve_iters = addr.iter() let resolve_iters = addr.iter()
.map(move |cmp| match cmp { .map(move |cmp| match cmp {
AddrComponent::DNS4(ref name) => { AddrComponent::DNS4(ref name) => {
future::Either::A(resolve_dns(name, resolver.clone(), ResolveTy::Dns4)) future::Either::A(resolve_dns(name, resolver.clone(), ResolveTy::Dns4))
} }
AddrComponent::DNS6(ref name) => { AddrComponent::DNS6(ref name) => {
future::Either::A(resolve_dns(name, resolver.clone(), ResolveTy::Dns6)) future::Either::A(resolve_dns(name, resolver.clone(), ResolveTy::Dns6))
} }
cmp => future::Either::B(future::ok(cmp)), cmp => future::Either::B(future::ok(cmp)),
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_iter(); .into_iter();
let new_addr = future::join_all(resolve_iters).map(move |outcome| { let new_addr = future::join_all(resolve_iters).map(move |outcome| {
let outcome: Multiaddr = outcome.into_iter().collect(); let outcome: Multiaddr = outcome.into_iter().collect();
debug!(target: "libp2p-dns", "DNS resolution outcome: {} => {}", addr, outcome); debug!(target: "libp2p-dns", "DNS resolution outcome: {} => {}", addr, outcome);
outcome outcome
}); });
let inner = self.inner; let inner = self.inner;
let future = new_addr let future = new_addr
.and_then(move |addr| { .and_then(move |addr| {
inner inner
.dial(addr) .dial(addr)
.map_err(|_| IoError::new(IoErrorKind::Other, "multiaddr not supported")) .map_err(|_| IoError::new(IoErrorKind::Other, "multiaddr not supported"))
}) })
.flatten(); .flatten();
Ok(Box::new(future) as Box<_>) Ok(Box::new(future) as Box<_>)
} }
#[inline] #[inline]
fn nat_traversal(&self, server: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> { fn nat_traversal(&self, server: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> {
// Since `listen_on` doesn't perform any resolution, we just pass through `nat_traversal` // Since `listen_on` doesn't perform any resolution, we just pass through `nat_traversal`
// as well. // as well.
self.inner.nat_traversal(server, observed) self.inner.nat_traversal(server, observed)
} }
} }
// How to resolve ; to an IPv4 address or an IPv6 address? // How to resolve ; to an IPv4 address or an IPv6 address?
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum ResolveTy { enum ResolveTy {
Dns4, Dns4,
Dns6, Dns6,
} }
// Resolve a DNS name and returns a future with the result. // Resolve a DNS name and returns a future with the result.
fn resolve_dns( fn resolve_dns(
name: &str, name: &str,
resolver: CpuPoolResolver, resolver: CpuPoolResolver,
ty: ResolveTy, ty: ResolveTy,
) -> Box<Future<Item = AddrComponent, Error = IoError>> { ) -> Box<Future<Item = AddrComponent, Error = IoError>> {
let debug_name = if log_enabled!(target: "libp2p-dns", Level::Trace) { let debug_name = if log_enabled!(target: "libp2p-dns", Level::Trace) {
Some(name.to_owned()) Some(name.to_owned())
} else { } else {
None None
}; };
let future = resolver.resolve(name).and_then(move |addrs| { let future = resolver.resolve(name).and_then(move |addrs| {
trace!(target: "libp2p-dns", "DNS component resolution: {} => {:?}", trace!(target: "libp2p-dns", "DNS component resolution: {} => {:?}",
debug_name.expect("trace log level was enabled"), addrs); debug_name.expect("trace log level was enabled"), addrs);
addrs addrs
.into_iter() .into_iter()
.filter_map(move |addr| match (addr, ty) { .filter_map(move |addr| match (addr, ty) {
(IpAddr::V4(addr), ResolveTy::Dns4) => Some(AddrComponent::IP4(addr)), (IpAddr::V4(addr), ResolveTy::Dns4) => Some(AddrComponent::IP4(addr)),
(IpAddr::V6(addr), ResolveTy::Dns6) => Some(AddrComponent::IP6(addr)), (IpAddr::V6(addr), ResolveTy::Dns6) => Some(AddrComponent::IP6(addr)),
_ => None, _ => None,
}) })
.next() .next()
.ok_or(IoError::new( .ok_or(IoError::new(
IoErrorKind::Other, IoErrorKind::Other,
"couldn't find any relevant IP address", "couldn't find any relevant IP address",
)) ))
}); });
Box::new(future) Box::new(future)
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate libp2p_tcp_transport; extern crate libp2p_tcp_transport;
use self::libp2p_tcp_transport::TcpConfig; use self::libp2p_tcp_transport::TcpConfig;
use DnsConfig; use DnsConfig;
use futures::{future, Future}; use futures::{future, Future};
use multiaddr::{AddrComponent, Multiaddr}; use multiaddr::{AddrComponent, Multiaddr};
use std::io::Error as IoError; use std::io::Error as IoError;
use swarm::Transport; use swarm::Transport;
#[test] #[test]
fn basic_resolve() { fn basic_resolve() {
#[derive(Clone)] #[derive(Clone)]
struct CustomTransport; struct CustomTransport;
impl Transport for CustomTransport { impl Transport for CustomTransport {
type RawConn = <TcpConfig as Transport>::RawConn; type RawConn = <TcpConfig as Transport>::RawConn;
type Listener = <TcpConfig as Transport>::Listener; type Listener = <TcpConfig as Transport>::Listener;
type ListenerUpgrade = <TcpConfig as Transport>::ListenerUpgrade; type ListenerUpgrade = <TcpConfig as Transport>::ListenerUpgrade;
type Dial = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>; type Dial = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>;
#[inline] #[inline]
fn listen_on( fn listen_on(
self, self,
_addr: Multiaddr, _addr: Multiaddr,
) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> { ) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> {
unreachable!() unreachable!()
} }
fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> { fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> {
let addr = addr.iter().collect::<Vec<_>>(); let addr = addr.iter().collect::<Vec<_>>();
assert_eq!(addr.len(), 2); assert_eq!(addr.len(), 2);
match addr[1] { match addr[1] {
AddrComponent::TCP(_) => (), AddrComponent::TCP(_) => (),
_ => panic!(), _ => panic!(),
}; };
match addr[0] { match addr[0] {
AddrComponent::DNS4(_) => (), AddrComponent::DNS4(_) => (),
AddrComponent::DNS6(_) => (), AddrComponent::DNS6(_) => (),
_ => panic!(), _ => panic!(),
}; };
Ok(Box::new(future::empty()) as Box<_>) Ok(Box::new(future::empty()) as Box<_>)
} }
#[inline] #[inline]
fn nat_traversal(&self, _: &Multiaddr, _: &Multiaddr) -> Option<Multiaddr> { fn nat_traversal(&self, _: &Multiaddr, _: &Multiaddr) -> Option<Multiaddr> {
panic!() panic!()
} }
} }
let transport = DnsConfig::new(CustomTransport); let transport = DnsConfig::new(CustomTransport);
let _ = transport let _ = transport
.clone() .clone()
.dial("/dns4/example.com/tcp/20000".parse().unwrap()) .dial("/dns4/example.com/tcp/20000".parse().unwrap())
.unwrap_or_else(|_| panic!()); .unwrap_or_else(|_| panic!());
let _ = transport let _ = transport
.dial("/dns6/example.com/tcp/20000".parse().unwrap()) .dial("/dns6/example.com/tcp/20000".parse().unwrap())
.unwrap_or_else(|_| panic!()); .unwrap_or_else(|_| panic!());
} }
} }

View File

@ -39,276 +39,283 @@ pub struct IdentifyProtocolConfig;
/// Output of the connection upgrade. /// Output of the connection upgrade.
pub enum IdentifyOutput<T> { pub enum IdentifyOutput<T> {
/// We obtained information from the remote. Happens when we are the dialer. /// We obtained information from the remote. Happens when we are the dialer.
RemoteInfo { RemoteInfo {
info: IdentifyInfo, info: IdentifyInfo,
/// Address the remote sees for us. /// Address the remote sees for us.
observed_addr: Multiaddr, observed_addr: Multiaddr,
}, },
/// We opened a connection to the remote and need to send it information. Happens when we are /// We opened a connection to the remote and need to send it information. Happens when we are
/// the listener. /// the listener.
Sender { Sender {
/// Object used to send identify info to the client. /// Object used to send identify info to the client.
sender: IdentifySender<T>, sender: IdentifySender<T>,
/// Observed multiaddress of the client. /// Observed multiaddress of the client.
observed_addr: Multiaddr, observed_addr: Multiaddr,
}, },
} }
/// Object used to send back information to the client. /// Object used to send back information to the client.
pub struct IdentifySender<T> { pub struct IdentifySender<T> {
inner: Framed<T, VarintCodec<Vec<u8>>>, inner: Framed<T, VarintCodec<Vec<u8>>>,
} }
impl<'a, T> IdentifySender<T> impl<'a, T> IdentifySender<T>
where where
T: AsyncWrite + 'a, T: AsyncWrite + 'a,
{ {
/// Sends back information to the client. Returns a future that is signalled whenever the /// Sends back information to the client. Returns a future that is signalled whenever the
/// info have been sent. /// info have been sent.
pub fn send( pub fn send(
self, self,
info: IdentifyInfo, info: IdentifyInfo,
observed_addr: &Multiaddr, observed_addr: &Multiaddr,
) -> Box<Future<Item = (), Error = IoError> + 'a> { ) -> Box<Future<Item = (), Error = IoError> + 'a> {
debug!(target: "libp2p-identify", "Sending identify info to client"); debug!(target: "libp2p-identify", "Sending identify info to client");
trace!(target: "libp2p-identify", "Sending: {:?}", info); trace!(target: "libp2p-identify", "Sending: {:?}", info);
let listen_addrs = info.listen_addrs let listen_addrs = info.listen_addrs
.into_iter() .into_iter()
.map(|addr| addr.into_bytes()) .map(|addr| addr.into_bytes())
.collect(); .collect();
let mut message = structs_proto::Identify::new(); let mut message = structs_proto::Identify::new();
message.set_agentVersion(info.agent_version); message.set_agentVersion(info.agent_version);
message.set_protocolVersion(info.protocol_version); message.set_protocolVersion(info.protocol_version);
message.set_publicKey(info.public_key); message.set_publicKey(info.public_key);
message.set_listenAddrs(listen_addrs); message.set_listenAddrs(listen_addrs);
message.set_observedAddr(observed_addr.to_bytes()); message.set_observedAddr(observed_addr.to_bytes());
message.set_protocols(RepeatedField::from_vec(info.protocols)); message.set_protocols(RepeatedField::from_vec(info.protocols));
let bytes = message let bytes = message
.write_to_bytes() .write_to_bytes()
.expect("writing protobuf failed ; should never happen"); .expect("writing protobuf failed ; should never happen");
let future = self.inner.send(bytes).map(|_| ()); let future = self.inner.send(bytes).map(|_| ());
Box::new(future) as Box<_> Box::new(future) as Box<_>
} }
} }
/// Information sent from the listener to the dialer. /// Information sent from the listener to the dialer.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct IdentifyInfo { pub struct IdentifyInfo {
/// Public key of the node in the DER format. /// Public key of the node in the DER format.
pub public_key: Vec<u8>, pub public_key: Vec<u8>,
/// Version of the "global" protocol, eg. `ipfs/1.0.0` or `polkadot/1.0.0`. /// Version of the "global" protocol, eg. `ipfs/1.0.0` or `polkadot/1.0.0`.
pub protocol_version: String, pub protocol_version: String,
/// Name and version of the client. Can be thought as similar to the `User-Agent` header /// Name and version of the client. Can be thought as similar to the `User-Agent` header
/// of HTTP. /// of HTTP.
pub agent_version: String, pub agent_version: String,
/// Addresses that the node is listening on. /// Addresses that the node is listening on.
pub listen_addrs: Vec<Multiaddr>, pub listen_addrs: Vec<Multiaddr>,
/// Protocols supported by the node, eg. `/ipfs/ping/1.0.0`. /// Protocols supported by the node, eg. `/ipfs/ping/1.0.0`.
pub protocols: Vec<String>, pub protocols: Vec<String>,
} }
impl<C> ConnectionUpgrade<C> for IdentifyProtocolConfig impl<C> ConnectionUpgrade<C> for IdentifyProtocolConfig
where where
C: AsyncRead + AsyncWrite + 'static, C: AsyncRead + AsyncWrite + 'static,
{ {
type NamesIter = iter::Once<(Bytes, Self::UpgradeIdentifier)>; type NamesIter = iter::Once<(Bytes, Self::UpgradeIdentifier)>;
type UpgradeIdentifier = (); type UpgradeIdentifier = ();
type Output = IdentifyOutput<C>; type Output = IdentifyOutput<C>;
type Future = Box<Future<Item = Self::Output, Error = IoError>>; type Future = Box<Future<Item = Self::Output, Error = IoError>>;
#[inline] #[inline]
fn protocol_names(&self) -> Self::NamesIter { fn protocol_names(&self) -> Self::NamesIter {
iter::once((Bytes::from("/ipfs/id/1.0.0"), ())) iter::once((Bytes::from("/ipfs/id/1.0.0"), ()))
} }
fn upgrade(self, socket: C, _: (), ty: Endpoint, observed_addr: &Multiaddr) -> Self::Future { fn upgrade(self, socket: C, _: (), ty: Endpoint, observed_addr: &Multiaddr) -> Self::Future {
trace!(target: "libp2p-identify", "Upgrading connection with {:?} as {:?}", trace!(target: "libp2p-identify", "Upgrading connection with {:?} as {:?}",
observed_addr, ty); observed_addr, ty);
let socket = socket.framed(VarintCodec::default()); let socket = socket.framed(VarintCodec::default());
let observed_addr_log = if log_enabled!(target: "libp2p-identify", Level::Debug) { let observed_addr_log = if log_enabled!(target: "libp2p-identify", Level::Debug) {
Some(observed_addr.clone()) Some(observed_addr.clone())
} else { } else {
None None
}; };
match ty { match ty {
Endpoint::Dialer => { Endpoint::Dialer => {
let future = socket let future = socket
.into_future() .into_future()
.map(|(msg, _)| msg) .map(|(msg, _)| msg)
.map_err(|(err, _)| err) .map_err(|(err, _)| err)
.and_then(|msg| { .and_then(|msg| {
debug!(target: "libp2p-identify", "Received identify message from {:?}", debug!(target: "libp2p-identify", "Received identify message from {:?}",
observed_addr_log observed_addr_log
.expect("Programmer error: expected `observed_addr_log' to be \ .expect("Programmer error: expected `observed_addr_log' to be \
non-None since debug log level is enabled")); non-None since debug log level is enabled"));
if let Some(msg) = msg { if let Some(msg) = msg {
let (info, observed_addr) = match parse_proto_msg(msg) { let (info, observed_addr) = match parse_proto_msg(msg) {
Ok(v) => v, Ok(v) => v,
Err(err) => { Err(err) => {
debug!(target: "libp2p-identify", debug!(target: "libp2p-identify",
"Failed to parse protobuf message ; error = {:?}", err); "Failed to parse protobuf message ; error = {:?}", err);
return Err(err.into()); return Err(err.into());
} }
}; };
trace!(target: "libp2p-identify", "Remote observes us as {:?}", trace!(target: "libp2p-identify", "Remote observes us as {:?}",
observed_addr); observed_addr);
trace!(target: "libp2p-identify", "Information received: {:?}", info); trace!(target: "libp2p-identify", "Information received: {:?}", info);
Ok(IdentifyOutput::RemoteInfo { Ok(IdentifyOutput::RemoteInfo {
info, info,
observed_addr, observed_addr,
}) })
} else {
debug!(target: "libp2p-identify", "Identify protocol stream closed \
before receiving info");
Err(IoErrorKind::InvalidData.into())
}
});
} else { Box::new(future) as Box<_>
debug!(target: "libp2p-identify", "Identify protocol stream closed \ }
before receiving info");
Err(IoErrorKind::InvalidData.into())
}
});
Box::new(future) as Box<_> Endpoint::Listener => {
} let sender = IdentifySender { inner: socket };
Endpoint::Listener => { let future = future::ok(IdentifyOutput::Sender {
let sender = IdentifySender { inner: socket }; sender,
observed_addr: observed_addr.clone(),
});
let future = future::ok(IdentifyOutput::Sender { Box::new(future) as Box<_>
sender, }
observed_addr: observed_addr.clone(), }
}); }
Box::new(future) as Box<_>
}
}
}
} }
// Turns a protobuf message into an `IdentifyInfo` and an observed address. If something bad // Turns a protobuf message into an `IdentifyInfo` and an observed address. If something bad
// happens, turn it into an `IoError`. // happens, turn it into an `IoError`.
fn parse_proto_msg(msg: BytesMut) -> Result<(IdentifyInfo, Multiaddr), IoError> { fn parse_proto_msg(msg: BytesMut) -> Result<(IdentifyInfo, Multiaddr), IoError> {
match protobuf_parse_from_bytes::<structs_proto::Identify>(&msg) { match protobuf_parse_from_bytes::<structs_proto::Identify>(&msg) {
Ok(mut msg) => { Ok(mut msg) => {
// Turn a `Vec<u8>` into a `Multiaddr`. If something bad happens, turn it into // Turn a `Vec<u8>` into a `Multiaddr`. If something bad happens, turn it into
// an `IoError`. // an `IoError`.
fn bytes_to_multiaddr(bytes: Vec<u8>) -> Result<Multiaddr, IoError> { fn bytes_to_multiaddr(bytes: Vec<u8>) -> Result<Multiaddr, IoError> {
Multiaddr::from_bytes(bytes) Multiaddr::from_bytes(bytes)
.map_err(|err| IoError::new(IoErrorKind::InvalidData, err)) .map_err(|err| IoError::new(IoErrorKind::InvalidData, err))
} }
let listen_addrs = { let listen_addrs = {
let mut addrs = Vec::new(); let mut addrs = Vec::new();
for addr in msg.take_listenAddrs().into_iter() { for addr in msg.take_listenAddrs().into_iter() {
addrs.push(bytes_to_multiaddr(addr)?); addrs.push(bytes_to_multiaddr(addr)?);
} }
addrs addrs
}; };
let observed_addr = bytes_to_multiaddr(msg.take_observedAddr())?; let observed_addr = bytes_to_multiaddr(msg.take_observedAddr())?;
let info = IdentifyInfo { let info = IdentifyInfo {
public_key: msg.take_publicKey(), public_key: msg.take_publicKey(),
protocol_version: msg.take_protocolVersion(), protocol_version: msg.take_protocolVersion(),
agent_version: msg.take_agentVersion(), agent_version: msg.take_agentVersion(),
listen_addrs: listen_addrs, listen_addrs: listen_addrs,
protocols: msg.take_protocols().into_vec(), protocols: msg.take_protocols().into_vec(),
}; };
Ok((info, observed_addr)) Ok((info, observed_addr))
} }
Err(err) => Err(IoError::new(IoErrorKind::InvalidData, err)), Err(err) => Err(IoError::new(IoErrorKind::InvalidData, err)),
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate libp2p_tcp_transport; extern crate libp2p_tcp_transport;
extern crate tokio_core; extern crate tokio_core;
use self::libp2p_tcp_transport::TcpConfig; use self::libp2p_tcp_transport::TcpConfig;
use self::tokio_core::reactor::Core; use self::tokio_core::reactor::Core;
use {IdentifyProtocolConfig, IdentifyOutput, IdentifyInfo}; use {IdentifyInfo, IdentifyOutput, IdentifyProtocolConfig};
use futures::{Future, Stream}; use futures::{Future, Stream};
use libp2p_swarm::Transport; use libp2p_swarm::Transport;
use std::sync::mpsc; use std::sync::mpsc;
use std::thread; use std::thread;
#[test] #[test]
fn correct_transfer() { fn correct_transfer() {
// We open a server and a client, send info from the server to the client, and check that // We open a server and a client, send info from the server to the client, and check that
// they were successfully received. // they were successfully received.
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
let bg_thread = thread::spawn(move || { let bg_thread = thread::spawn(move || {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let transport = TcpConfig::new(core.handle()) let transport = TcpConfig::new(core.handle()).with_upgrade(IdentifyProtocolConfig);
.with_upgrade(IdentifyProtocolConfig);
let (listener, addr) = transport let (listener, addr) = transport
.listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap()).unwrap(); .listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap())
tx.send(addr).unwrap(); .unwrap();
tx.send(addr).unwrap();
let future = listener let future = listener
.into_future() .into_future()
.map_err(|(err, _)| err) .map_err(|(err, _)| err)
.and_then(|(client, _)| client.unwrap().map(|v| v.0)) .and_then(|(client, _)| client.unwrap().map(|v| v.0))
.and_then(|identify| { .and_then(|identify| match identify {
match identify { IdentifyOutput::Sender { sender, .. } => sender.send(
IdentifyOutput::Sender { sender, .. } => { IdentifyInfo {
sender.send(IdentifyInfo { public_key: vec![1, 2, 3, 4, 5, 7],
public_key: vec![1, 2, 3, 4, 5, 7], protocol_version: "proto_version".to_owned(),
protocol_version: "proto_version".to_owned(), agent_version: "agent_version".to_owned(),
agent_version: "agent_version".to_owned(), listen_addrs: vec![
listen_addrs: vec![ "/ip4/80.81.82.83/tcp/500".parse().unwrap(),
"/ip4/80.81.82.83/tcp/500".parse().unwrap(), "/ip6/::1/udp/1000".parse().unwrap(),
"/ip6/::1/udp/1000".parse().unwrap() ],
], protocols: vec!["proto1".to_string(), "proto2".to_string()],
protocols: vec!["proto1".to_string(), "proto2".to_string()], },
}, &"/ip4/100.101.102.103/tcp/5000".parse().unwrap()) &"/ip4/100.101.102.103/tcp/5000".parse().unwrap(),
}, ),
_ => panic!() _ => panic!(),
} });
});
let _ = core.run(future).unwrap(); let _ = core.run(future).unwrap();
}); });
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let transport = TcpConfig::new(core.handle()) let transport = TcpConfig::new(core.handle()).with_upgrade(IdentifyProtocolConfig);
.with_upgrade(IdentifyProtocolConfig);
let future = transport let future = transport
.dial(rx.recv().unwrap()) .dial(rx.recv().unwrap())
.unwrap_or_else(|_| panic!()) .unwrap_or_else(|_| panic!())
.and_then(|(identify, _)| { .and_then(|(identify, _)| match identify {
match identify { IdentifyOutput::RemoteInfo {
IdentifyOutput::RemoteInfo { info, observed_addr } => { info,
assert_eq!(observed_addr, "/ip4/100.101.102.103/tcp/5000".parse().unwrap()); observed_addr,
assert_eq!(info.public_key, &[1, 2, 3, 4, 5, 7]); } => {
assert_eq!(info.protocol_version, "proto_version"); assert_eq!(
assert_eq!(info.agent_version, "agent_version"); observed_addr,
assert_eq!(info.listen_addrs, &[ "/ip4/100.101.102.103/tcp/5000".parse().unwrap()
"/ip4/80.81.82.83/tcp/500".parse().unwrap(), );
"/ip6/::1/udp/1000".parse().unwrap() assert_eq!(info.public_key, &[1, 2, 3, 4, 5, 7]);
]); assert_eq!(info.protocol_version, "proto_version");
assert_eq!(info.protocols, &["proto1".to_string(), "proto2".to_string()]); assert_eq!(info.agent_version, "agent_version");
Ok(()) assert_eq!(
}, info.listen_addrs,
_ => panic!() &[
} "/ip4/80.81.82.83/tcp/500".parse().unwrap(),
}); "/ip6/::1/udp/1000".parse().unwrap()
]
);
assert_eq!(
info.protocols,
&["proto1".to_string(), "proto2".to_string()]
);
Ok(())
}
_ => panic!(),
});
let _ = core.run(future).unwrap(); let _ = core.run(future).unwrap();
bg_thread.join().unwrap(); bg_thread.join().unwrap();
} }
} }

View File

@ -30,361 +30,358 @@ use std::time::Duration;
/// Implementation of `Transport`. See [the crate root description](index.html). /// Implementation of `Transport`. See [the crate root description](index.html).
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct IdentifyTransport<Trans, PStoreRef> { pub struct IdentifyTransport<Trans, PStoreRef> {
transport: Trans, transport: Trans,
peerstore: PStoreRef, peerstore: PStoreRef,
addr_ttl: Duration, addr_ttl: Duration,
} }
impl<Trans, PStoreRef> IdentifyTransport<Trans, PStoreRef> { impl<Trans, PStoreRef> IdentifyTransport<Trans, PStoreRef> {
/// Creates an `IdentifyTransport` that wraps around the given transport and peerstore. /// Creates an `IdentifyTransport` that wraps around the given transport and peerstore.
#[inline] #[inline]
pub fn new(transport: Trans, peerstore: PStoreRef) -> Self { pub fn new(transport: Trans, peerstore: PStoreRef) -> Self {
IdentifyTransport::with_ttl(transport, peerstore, Duration::from_secs(3600)) IdentifyTransport::with_ttl(transport, peerstore, Duration::from_secs(3600))
} }
/// Same as `new`, but allows specifying a time-to-live for the addresses gathered from /// Same as `new`, but allows specifying a time-to-live for the addresses gathered from
/// remotes that connect to us. /// remotes that connect to us.
/// ///
/// The default value is one hour. /// The default value is one hour.
#[inline] #[inline]
pub fn with_ttl(transport: Trans, peerstore: PStoreRef, ttl: Duration) -> Self { pub fn with_ttl(transport: Trans, peerstore: PStoreRef, ttl: Duration) -> Self {
IdentifyTransport { IdentifyTransport {
transport: transport, transport: transport,
peerstore: peerstore, peerstore: peerstore,
addr_ttl: ttl, addr_ttl: ttl,
} }
} }
} }
impl<Trans, PStore, PStoreRef> Transport for IdentifyTransport<Trans, PStoreRef> impl<Trans, PStore, PStoreRef> Transport for IdentifyTransport<Trans, PStoreRef>
where where
Trans: Transport + Clone + 'static, // TODO: 'static :( Trans: Transport + Clone + 'static, // TODO: 'static :(
PStoreRef: Deref<Target = PStore> + Clone + 'static, // TODO: 'static :( PStoreRef: Deref<Target = PStore> + Clone + 'static, // TODO: 'static :(
for<'r> &'r PStore: Peerstore, for<'r> &'r PStore: Peerstore,
{ {
type RawConn = Trans::RawConn; type RawConn = Trans::RawConn;
type Listener = Box<Stream<Item = Self::ListenerUpgrade, Error = IoError>>; type Listener = Box<Stream<Item = Self::ListenerUpgrade, Error = IoError>>;
type ListenerUpgrade = Box<Future<Item = (Trans::RawConn, Multiaddr), Error = IoError>>; type ListenerUpgrade = Box<Future<Item = (Trans::RawConn, Multiaddr), Error = IoError>>;
type Dial = Box<Future<Item = (Trans::RawConn, Multiaddr), Error = IoError>>; type Dial = Box<Future<Item = (Trans::RawConn, Multiaddr), Error = IoError>>;
#[inline] #[inline]
fn listen_on(self, addr: Multiaddr) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> { fn listen_on(self, addr: Multiaddr) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> {
// Note that `listen_on` expects a "regular" multiaddr (eg. `/ip/.../tcp/...`), // Note that `listen_on` expects a "regular" multiaddr (eg. `/ip/.../tcp/...`),
// and not `/p2p/<foo>`. // and not `/p2p/<foo>`.
let (listener, new_addr) = match self.transport.clone().listen_on(addr.clone()) { let (listener, new_addr) = match self.transport.clone().listen_on(addr.clone()) {
Ok((l, a)) => (l, a), Ok((l, a)) => (l, a),
Err((inner, addr)) => { Err((inner, addr)) => {
let id = IdentifyTransport { let id = IdentifyTransport {
transport: inner, transport: inner,
peerstore: self.peerstore, peerstore: self.peerstore,
addr_ttl: self.addr_ttl, addr_ttl: self.addr_ttl,
}; };
return Err((id, addr)); return Err((id, addr));
} }
}; };
let identify_upgrade = self.transport.with_upgrade(IdentifyProtocolConfig); let identify_upgrade = self.transport.with_upgrade(IdentifyProtocolConfig);
let peerstore = self.peerstore; let peerstore = self.peerstore;
let addr_ttl = self.addr_ttl; let addr_ttl = self.addr_ttl;
let listener = listener.map(move |connec| { let listener = listener.map(move |connec| {
let peerstore = peerstore.clone(); let peerstore = peerstore.clone();
let identify_upgrade = identify_upgrade.clone(); let identify_upgrade = identify_upgrade.clone();
let fut = connec let fut = connec
.and_then(move |(connec, client_addr)| { .and_then(move |(connec, client_addr)| {
// Dial the address that connected to us and try upgrade with the // Dial the address that connected to us and try upgrade with the
// identify protocol. // identify protocol.
identify_upgrade identify_upgrade
.clone() .clone()
.dial(client_addr.clone()) .dial(client_addr.clone())
.map_err(|_| { .map_err(|_| {
IoError::new(IoErrorKind::Other, "couldn't dial back incoming node") IoError::new(IoErrorKind::Other, "couldn't dial back incoming node")
}) })
.map(move |id| (id, connec)) .map(move |id| (id, connec))
}) })
.and_then(move |(dial, connec)| dial.map(move |dial| (dial, connec))) .and_then(move |(dial, connec)| dial.map(move |dial| (dial, connec)))
.and_then(move |((identify, original_addr), connec)| { .and_then(move |((identify, original_addr), connec)| {
// Compute the "real" address of the node (in the form `/p2p/...`) and add // Compute the "real" address of the node (in the form `/p2p/...`) and add
// it to the peerstore. // it to the peerstore.
let real_addr = match identify { let real_addr = match identify {
IdentifyOutput::RemoteInfo { info, .. } => process_identify_info( IdentifyOutput::RemoteInfo { info, .. } => process_identify_info(
&info, &info,
&*peerstore.clone(), &*peerstore.clone(),
original_addr, original_addr,
addr_ttl, addr_ttl,
)?, )?,
_ => unreachable!( _ => unreachable!(
"the identify protocol guarantees that we receive \ "the identify protocol guarantees that we receive \
remote information when we dial a node" remote information when we dial a node"
), ),
}; };
Ok((connec, real_addr)) Ok((connec, real_addr))
}); });
Box::new(fut) as Box<Future<Item = _, Error = _>> Box::new(fut) as Box<Future<Item = _, Error = _>>
}); });
Ok((Box::new(listener) as Box<_>, new_addr)) Ok((Box::new(listener) as Box<_>, new_addr))
} }
#[inline] #[inline]
fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> { fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> {
match multiaddr_to_peerid(addr.clone()) { match multiaddr_to_peerid(addr.clone()) {
Ok(peer_id) => { Ok(peer_id) => {
// If the multiaddress is a peer ID, try each known multiaddress (taken from the // If the multiaddress is a peer ID, try each known multiaddress (taken from the
// peerstore) one by one. // peerstore) one by one.
let addrs = self.peerstore let addrs = self.peerstore
.deref() .deref()
.peer(&peer_id) .peer(&peer_id)
.into_iter() .into_iter()
.flat_map(|peer| peer.addrs()) .flat_map(|peer| peer.addrs())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_iter(); .into_iter();
let transport = self.transport; let transport = self.transport;
let future = stream::iter_ok(addrs) let future = stream::iter_ok(addrs)
// Try to dial each address through the transport. // Try to dial each address through the transport.
.filter_map(move |addr| transport.clone().dial(addr).ok()) .filter_map(move |addr| transport.clone().dial(addr).ok())
.and_then(move |dial| dial) .and_then(move |dial| dial)
// Pick the first non-failing dial result. // Pick the first non-failing dial result.
.then(|res| Ok(res)) .then(|res| Ok(res))
.filter_map(|res| res.ok()) .filter_map(|res| res.ok())
.into_future() .into_future()
.map_err(|(err, _)| err) .map_err(|(err, _)| err)
.and_then(|(val, _)| val.ok_or(IoErrorKind::InvalidData.into())) // TODO: wrong error .and_then(|(val, _)| val.ok_or(IoErrorKind::InvalidData.into())) // TODO: wrong error
.map(move |(socket, _inner_client_addr)| (socket, addr)); .map(move |(socket, _inner_client_addr)| (socket, addr));
Ok(Box::new(future) as Box<_>) Ok(Box::new(future) as Box<_>)
} }
Err(addr) => { Err(addr) => {
// If the multiaddress is something else, propagate it to the underlying transport // If the multiaddress is something else, propagate it to the underlying transport
// and identify the node. // and identify the node.
let transport = self.transport; let transport = self.transport;
let identify_upgrade = transport.clone().with_upgrade(IdentifyProtocolConfig); let identify_upgrade = transport.clone().with_upgrade(IdentifyProtocolConfig);
// We dial a first time the node and upgrade it to identify. // We dial a first time the node and upgrade it to identify.
let dial = match identify_upgrade.dial(addr) { let dial = match identify_upgrade.dial(addr) {
Ok(d) => d, Ok(d) => d,
Err((_, addr)) => { Err((_, addr)) => {
let id = IdentifyTransport { let id = IdentifyTransport {
transport, transport,
peerstore: self.peerstore, peerstore: self.peerstore,
addr_ttl: self.addr_ttl, addr_ttl: self.addr_ttl,
}; };
return Err((id, addr)); return Err((id, addr));
} }
}; };
let peerstore = self.peerstore; let peerstore = self.peerstore;
let addr_ttl = self.addr_ttl; let addr_ttl = self.addr_ttl;
let future = dial.and_then(move |identify| { let future = dial.and_then(move |identify| {
// On success, store the information in the peerstore and compute the // On success, store the information in the peerstore and compute the
// "real" address of the node (of the form `/p2p/...`). // "real" address of the node (of the form `/p2p/...`).
let (real_addr, old_addr); let (real_addr, old_addr);
match identify { match identify {
(IdentifyOutput::RemoteInfo { info, .. }, a) => { (IdentifyOutput::RemoteInfo { info, .. }, a) => {
old_addr = a.clone(); old_addr = a.clone();
real_addr = process_identify_info(&info, &*peerstore, a, addr_ttl)?; real_addr = process_identify_info(&info, &*peerstore, a, addr_ttl)?;
} }
_ => unreachable!( _ => unreachable!(
"the identify protocol guarantees that we receive \ "the identify protocol guarantees that we receive \
remote information when we dial a node" remote information when we dial a node"
), ),
}; };
// Then dial the same node again. // Then dial the same node again.
Ok(transport Ok(transport
.dial(old_addr) .dial(old_addr)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
panic!("the same multiaddr was determined to be valid earlier") panic!("the same multiaddr was determined to be valid earlier")
}) })
.into_future() .into_future()
.map(move |(dial, _wrong_addr)| (dial, real_addr))) .map(move |(dial, _wrong_addr)| (dial, real_addr)))
}).flatten(); }).flatten();
Ok(Box::new(future) as Box<_>) Ok(Box::new(future) as Box<_>)
} }
} }
} }
#[inline] #[inline]
fn nat_traversal(&self, a: &Multiaddr, b: &Multiaddr) -> Option<Multiaddr> { fn nat_traversal(&self, a: &Multiaddr, b: &Multiaddr) -> Option<Multiaddr> {
self.transport.nat_traversal(a, b) self.transport.nat_traversal(a, b)
} }
} }
impl<Trans, PStore, PStoreRef> MuxedTransport for IdentifyTransport<Trans, PStoreRef> impl<Trans, PStore, PStoreRef> MuxedTransport for IdentifyTransport<Trans, PStoreRef>
where where
Trans: MuxedTransport + Clone + 'static, Trans: MuxedTransport + Clone + 'static,
PStoreRef: Deref<Target = PStore> + Clone + 'static, PStoreRef: Deref<Target = PStore> + Clone + 'static,
for<'r> &'r PStore: Peerstore, for<'r> &'r PStore: Peerstore,
{ {
type Incoming = Box<Future<Item = Self::IncomingUpgrade, Error = IoError>>; type Incoming = Box<Future<Item = Self::IncomingUpgrade, Error = IoError>>;
type IncomingUpgrade = Box<Future<Item = (Trans::RawConn, Multiaddr), Error = IoError>>; type IncomingUpgrade = Box<Future<Item = (Trans::RawConn, Multiaddr), Error = IoError>>;
#[inline] #[inline]
fn next_incoming(self) -> Self::Incoming { fn next_incoming(self) -> Self::Incoming {
let identify_upgrade = self.transport.clone().with_upgrade(IdentifyProtocolConfig); let identify_upgrade = self.transport.clone().with_upgrade(IdentifyProtocolConfig);
let peerstore = self.peerstore; let peerstore = self.peerstore;
let addr_ttl = self.addr_ttl; let addr_ttl = self.addr_ttl;
let future = self.transport let future = self.transport.next_incoming().map(move |incoming| {
.next_incoming() let future = incoming
.map(move |incoming| { .and_then(move |(connec, client_addr)| {
let future = incoming // On an incoming connection, dial back the node and upgrade to the identify
.and_then(move |(connec, client_addr)| { // protocol.
// On an incoming connection, dial back the node and upgrade to the identify identify_upgrade
// protocol. .clone()
identify_upgrade .dial(client_addr.clone())
.clone() .map_err(|_| {
.dial(client_addr.clone()) IoError::new(IoErrorKind::Other, "couldn't dial back incoming node")
.map_err(|_| { })
IoError::new(IoErrorKind::Other, "couldn't dial back incoming node") .map(move |id| (id, connec))
}) })
.map(move |id| (id, connec)) .and_then(move |(dial, connec)| dial.map(move |dial| (dial, connec)))
}) .and_then(move |(identify, connec)| {
.and_then(move |(dial, connec)| dial.map(move |dial| (dial, connec))) // Add the info to the peerstore and compute the "real" address of the node (in
.and_then(move |(identify, connec)| { // the form `/p2p/...`).
// Add the info to the peerstore and compute the "real" address of the node (in let real_addr = match identify {
// the form `/p2p/...`). (IdentifyOutput::RemoteInfo { info, .. }, old_addr) => {
let real_addr = match identify { process_identify_info(&info, &*peerstore, old_addr, addr_ttl)?
(IdentifyOutput::RemoteInfo { info, .. }, old_addr) => { }
process_identify_info(&info, &*peerstore, old_addr, addr_ttl)? _ => unreachable!(
} "the identify protocol guarantees that we receive remote \
_ => unreachable!( information when we dial a node"
"the identify protocol guarantees that we receive remote \ ),
information when we dial a node" };
),
};
Ok((connec, real_addr)) Ok((connec, real_addr))
}); });
Box::new(future) as Box<Future<Item = _, Error = _>> Box::new(future) as Box<Future<Item = _, Error = _>>
}); });
Box::new(future) as Box<_> Box::new(future) as Box<_>
} }
} }
// If the multiaddress is in the form `/p2p/...`, turn it into a `PeerId`. // If the multiaddress is in the form `/p2p/...`, turn it into a `PeerId`.
// Otherwise, return it as-is. // Otherwise, return it as-is.
fn multiaddr_to_peerid(addr: Multiaddr) -> Result<PeerId, Multiaddr> { fn multiaddr_to_peerid(addr: Multiaddr) -> Result<PeerId, Multiaddr> {
let components = addr.iter().collect::<Vec<_>>(); let components = addr.iter().collect::<Vec<_>>();
if components.len() < 1 { if components.len() < 1 {
return Err(addr); return Err(addr);
} }
match components.last() { match components.last() {
Some(&AddrComponent::P2P(ref peer_id)) | Some(&AddrComponent::P2P(ref peer_id)) | Some(&AddrComponent::IPFS(ref peer_id)) => {
Some(&AddrComponent::IPFS(ref peer_id)) => { // TODO: `peer_id` is sometimes in fact a CID here
// TODO: `peer_id` is sometimes in fact a CID here match PeerId::from_bytes(peer_id.clone()) {
match PeerId::from_bytes(peer_id.clone()) { Ok(peer_id) => Ok(peer_id),
Ok(peer_id) => Ok(peer_id), Err(_) => Err(addr),
Err(_) => Err(addr), }
} }
} _ => Err(addr),
_ => Err(addr), }
}
} }
// When passed the information sent by a remote, inserts the remote into the given peerstore and // When passed the information sent by a remote, inserts the remote into the given peerstore and
// returns a multiaddr of the format `/p2p/...` corresponding to this node. // returns a multiaddr of the format `/p2p/...` corresponding to this node.
// //
// > **Note**: This function is highly-specific, but this precise behaviour is needed in multiple // > **Note**: This function is highly-specific, but this precise behaviour is needed in multiple
// > different places in the code. // > different places in the code.
fn process_identify_info<P>( fn process_identify_info<P>(
info: &IdentifyInfo, info: &IdentifyInfo,
peerstore: P, peerstore: P,
client_addr: Multiaddr, client_addr: Multiaddr,
ttl: Duration, ttl: Duration,
) -> Result<Multiaddr, IoError> ) -> Result<Multiaddr, IoError>
where where
P: Peerstore, P: Peerstore,
{ {
let peer_id = PeerId::from_public_key(&info.public_key); let peer_id = PeerId::from_public_key(&info.public_key);
peerstore peerstore
.peer_or_create(&peer_id) .peer_or_create(&peer_id)
.add_addr(client_addr, ttl); .add_addr(client_addr, ttl);
Ok(AddrComponent::P2P(peer_id.into_bytes()).into()) Ok(AddrComponent::P2P(peer_id.into_bytes()).into())
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate libp2p_tcp_transport; extern crate libp2p_tcp_transport;
extern crate tokio_core; extern crate tokio_core;
use self::libp2p_tcp_transport::TcpConfig; use self::libp2p_tcp_transport::TcpConfig;
use self::tokio_core::reactor::Core; use self::tokio_core::reactor::Core;
use IdentifyTransport; use IdentifyTransport;
use futures::{Future, Stream}; use futures::{Future, Stream};
use libp2p_peerstore::{PeerAccess, PeerId, Peerstore}; use libp2p_peerstore::{PeerAccess, PeerId, Peerstore};
use libp2p_peerstore::memory_peerstore::MemoryPeerstore; use libp2p_peerstore::memory_peerstore::MemoryPeerstore;
use libp2p_swarm::Transport; use libp2p_swarm::Transport;
use multiaddr::{AddrComponent, Multiaddr}; use multiaddr::{AddrComponent, Multiaddr};
use std::io::Error as IoError; use std::io::Error as IoError;
use std::iter; use std::iter;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
#[test] #[test]
fn dial_peer_id() { fn dial_peer_id() {
// When we dial an `/p2p/...` address, the `IdentifyTransport` should look into the // When we dial an `/p2p/...` address, the `IdentifyTransport` should look into the
// peerstore and dial one of the known multiaddresses of the node instead. // peerstore and dial one of the known multiaddresses of the node instead.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct UnderlyingTrans { struct UnderlyingTrans {
inner: TcpConfig, inner: TcpConfig,
} }
impl Transport for UnderlyingTrans { impl Transport for UnderlyingTrans {
type RawConn = <TcpConfig as Transport>::RawConn; type RawConn = <TcpConfig as Transport>::RawConn;
type Listener = Box<Stream<Item = Self::ListenerUpgrade, Error = IoError>>; type Listener = Box<Stream<Item = Self::ListenerUpgrade, Error = IoError>>;
type ListenerUpgrade = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>; type ListenerUpgrade = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>;
type Dial = <TcpConfig as Transport>::Dial; type Dial = <TcpConfig as Transport>::Dial;
#[inline] #[inline]
fn listen_on( fn listen_on(
self, self,
_: Multiaddr, _: Multiaddr,
) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> { ) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> {
unreachable!() unreachable!()
} }
#[inline] #[inline]
fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> { fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> {
assert_eq!( assert_eq!(
addr, addr,
"/ip4/127.0.0.1/tcp/12345".parse::<Multiaddr>().unwrap() "/ip4/127.0.0.1/tcp/12345".parse::<Multiaddr>().unwrap()
); );
Ok(self.inner.dial(addr).unwrap_or_else(|_| panic!())) Ok(self.inner.dial(addr).unwrap_or_else(|_| panic!()))
} }
#[inline] #[inline]
fn nat_traversal(&self, a: &Multiaddr, b: &Multiaddr) -> Option<Multiaddr> { fn nat_traversal(&self, a: &Multiaddr, b: &Multiaddr) -> Option<Multiaddr> {
self.inner.nat_traversal(a, b) self.inner.nat_traversal(a, b)
} }
} }
let peer_id = PeerId::from_public_key(&vec![1, 2, 3, 4]); let peer_id = PeerId::from_public_key(&vec![1, 2, 3, 4]);
let peerstore = MemoryPeerstore::empty(); let peerstore = MemoryPeerstore::empty();
peerstore.peer_or_create(&peer_id).add_addr( peerstore.peer_or_create(&peer_id).add_addr(
"/ip4/127.0.0.1/tcp/12345".parse().unwrap(), "/ip4/127.0.0.1/tcp/12345".parse().unwrap(),
Duration::from_secs(3600), Duration::from_secs(3600),
); );
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let underlying = UnderlyingTrans { let underlying = UnderlyingTrans {
inner: TcpConfig::new(core.handle()), inner: TcpConfig::new(core.handle()),
}; };
let transport = IdentifyTransport::new(underlying, Arc::new(peerstore)); let transport = IdentifyTransport::new(underlying, Arc::new(peerstore));
let future = transport let future = transport
.dial(iter::once(AddrComponent::P2P(peer_id.into_bytes())).collect()) .dial(iter::once(AddrComponent::P2P(peer_id.into_bytes())).collect())
.unwrap_or_else(|_| panic!()) .unwrap_or_else(|_| panic!())
.then::<_, Result<(), ()>>(|_| Ok(())); .then::<_, Result<(), ()>>(|_| Ok(()));
let _ = core.run(future).unwrap(); let _ = core.run(future).unwrap();
} }
} }

View File

@ -23,11 +23,11 @@
use super::TTL; use super::TTL;
use PeerId; use PeerId;
use base58::{FromBase58, ToBase58}; use base58::{FromBase58, ToBase58};
use datastore::{Datastore, Query, JsonFileDatastore, JsonFileDatastoreEntry}; use datastore::{Datastore, JsonFileDatastore, JsonFileDatastoreEntry, Query};
use futures::{Future, Stream}; use futures::{Future, Stream};
use multiaddr::Multiaddr; use multiaddr::Multiaddr;
use peer_info::{PeerInfo, AddAddrBehaviour}; use peer_info::{AddAddrBehaviour, PeerInfo};
use peerstore::{Peerstore, PeerAccess}; use peerstore::{PeerAccess, Peerstore};
use std::io::Error as IoError; use std::io::Error as IoError;
use std::iter; use std::iter;
use std::path::PathBuf; use std::path::PathBuf;
@ -35,106 +35,111 @@ use std::vec::IntoIter as VecIntoIter;
/// Peerstore backend that uses a Json file. /// Peerstore backend that uses a Json file.
pub struct JsonPeerstore { pub struct JsonPeerstore {
store: JsonFileDatastore<PeerInfo>, store: JsonFileDatastore<PeerInfo>,
} }
impl JsonPeerstore { impl JsonPeerstore {
/// Opens a new peerstore tied to a JSON file at the given path. /// Opens a new peerstore tied to a JSON file at the given path.
/// ///
/// If the file exists, this function will open it. In any case, flushing the peerstore or /// If the file exists, this function will open it. In any case, flushing the peerstore or
/// destroying it will write to the file. /// destroying it will write to the file.
#[inline] #[inline]
pub fn new<P>(path: P) -> Result<JsonPeerstore, IoError> pub fn new<P>(path: P) -> Result<JsonPeerstore, IoError>
where P: Into<PathBuf> where
{ P: Into<PathBuf>,
Ok(JsonPeerstore { store: JsonFileDatastore::new(path)? }) {
} Ok(JsonPeerstore {
store: JsonFileDatastore::new(path)?,
})
}
/// Flushes the content of the peer store to the disk. /// Flushes the content of the peer store to the disk.
/// ///
/// This function can only fail in case of a disk access error. If an error occurs, any change /// This function can only fail in case of a disk access error. If an error occurs, any change
/// to the peerstore that was performed since the last successful flush will be lost. No data /// to the peerstore that was performed since the last successful flush will be lost. No data
/// will be corrupted. /// will be corrupted.
#[inline] #[inline]
pub fn flush(&self) -> Result<(), IoError> { pub fn flush(&self) -> Result<(), IoError> {
self.store.flush() self.store.flush()
} }
} }
impl<'a> Peerstore for &'a JsonPeerstore { impl<'a> Peerstore for &'a JsonPeerstore {
type PeerAccess = JsonPeerstoreAccess<'a>; type PeerAccess = JsonPeerstoreAccess<'a>;
type PeersIter = Box<Iterator<Item = PeerId>>; type PeersIter = Box<Iterator<Item = PeerId>>;
#[inline] #[inline]
fn peer(self, peer_id: &PeerId) -> Option<Self::PeerAccess> { fn peer(self, peer_id: &PeerId) -> Option<Self::PeerAccess> {
let hash = peer_id.as_bytes().to_base58(); let hash = peer_id.as_bytes().to_base58();
self.store.lock(hash.into()).map(JsonPeerstoreAccess) self.store.lock(hash.into()).map(JsonPeerstoreAccess)
} }
#[inline] #[inline]
fn peer_or_create(self, peer_id: &PeerId) -> Self::PeerAccess { fn peer_or_create(self, peer_id: &PeerId) -> Self::PeerAccess {
let hash = peer_id.as_bytes().to_base58(); let hash = peer_id.as_bytes().to_base58();
JsonPeerstoreAccess(self.store.lock_or_create(hash.into())) JsonPeerstoreAccess(self.store.lock_or_create(hash.into()))
} }
fn peers(self) -> Self::PeersIter { fn peers(self) -> Self::PeersIter {
let query = self.store.query(Query { let query = self.store.query(Query {
prefix: "".into(), prefix: "".into(),
filters: vec![], filters: vec![],
orders: vec![], orders: vec![],
skip: 0, skip: 0,
limit: u64::max_value(), limit: u64::max_value(),
keys_only: true, keys_only: true,
}); });
let list = query.filter_map(|(key, _)| { let list = query
// We filter out invalid elements. This can happen if the JSON storage file was .filter_map(|(key, _)| {
// corrupted or manually modified by the user. // We filter out invalid elements. This can happen if the JSON storage file was
PeerId::from_bytes(key.from_base58().ok()?).ok() // corrupted or manually modified by the user.
}) PeerId::from_bytes(key.from_base58().ok()?).ok()
.collect() })
.wait(); // Wait can never block for the JSON datastore. .collect()
.wait(); // Wait can never block for the JSON datastore.
// Need to handle I/O errors. Again we just ignore. // Need to handle I/O errors. Again we just ignore.
if let Ok(list) = list { if let Ok(list) = list {
Box::new(list.into_iter()) as Box<_> Box::new(list.into_iter()) as Box<_>
} else { } else {
Box::new(iter::empty()) as Box<_> Box::new(iter::empty()) as Box<_>
} }
} }
} }
pub struct JsonPeerstoreAccess<'a>(JsonFileDatastoreEntry<'a, PeerInfo>); pub struct JsonPeerstoreAccess<'a>(JsonFileDatastoreEntry<'a, PeerInfo>);
impl<'a> PeerAccess for JsonPeerstoreAccess<'a> { impl<'a> PeerAccess for JsonPeerstoreAccess<'a> {
type AddrsIter = VecIntoIter<Multiaddr>; type AddrsIter = VecIntoIter<Multiaddr>;
#[inline] #[inline]
fn addrs(&self) -> Self::AddrsIter { fn addrs(&self) -> Self::AddrsIter {
self.0.addrs().cloned().collect::<Vec<_>>().into_iter() self.0.addrs().cloned().collect::<Vec<_>>().into_iter()
} }
#[inline] #[inline]
fn add_addr(&mut self, addr: Multiaddr, ttl: TTL) { fn add_addr(&mut self, addr: Multiaddr, ttl: TTL) {
self.0.add_addr(addr, ttl, AddAddrBehaviour::IgnoreTtlIfInferior); self.0
} .add_addr(addr, ttl, AddAddrBehaviour::IgnoreTtlIfInferior);
}
#[inline] #[inline]
fn set_addr_ttl(&mut self, addr: Multiaddr, ttl: TTL) { fn set_addr_ttl(&mut self, addr: Multiaddr, ttl: TTL) {
self.0.add_addr(addr, ttl, AddAddrBehaviour::OverwriteTtl); self.0.add_addr(addr, ttl, AddAddrBehaviour::OverwriteTtl);
} }
#[inline] #[inline]
fn clear_addrs(&mut self) { fn clear_addrs(&mut self) {
self.0.set_addrs(iter::empty()); self.0.set_addrs(iter::empty());
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate tempfile; extern crate tempfile;
peerstore_tests!( peerstore_tests!(
{::json_peerstore::JsonPeerstore::new(temp_file.path()).unwrap()} {::json_peerstore::JsonPeerstore::new(temp_file.path()).unwrap()}
{let temp_file = self::tempfile::NamedTempFile::new().unwrap()} {let temp_file = self::tempfile::NamedTempFile::new().unwrap()}
); );
} }

View File

@ -35,21 +35,21 @@
//! data rather than returning the error. //! data rather than returning the error.
//! //!
//! # Example //! # Example
//! //!
//! ``` //! ```
//! extern crate multiaddr; //! extern crate multiaddr;
//! extern crate libp2p_peerstore; //! extern crate libp2p_peerstore;
//! //!
//! # fn main() { //! # fn main() {
//! use libp2p_peerstore::memory_peerstore::MemoryPeerstore; //! use libp2p_peerstore::memory_peerstore::MemoryPeerstore;
//! use libp2p_peerstore::{PeerId, Peerstore, PeerAccess}; //! use libp2p_peerstore::{PeerId, Peerstore, PeerAccess};
//! use multiaddr::Multiaddr; //! use multiaddr::Multiaddr;
//! use std::time::Duration; //! use std::time::Duration;
//! //!
//! // In this example we use a `MemoryPeerstore`, but you can easily swap it for another backend. //! // In this example we use a `MemoryPeerstore`, but you can easily swap it for another backend.
//! let mut peerstore = MemoryPeerstore::empty(); //! let mut peerstore = MemoryPeerstore::empty();
//! let peer_id = PeerId::from_public_key(&[1, 2, 3, 4]); //! let peer_id = PeerId::from_public_key(&[1, 2, 3, 4]);
//! //!
//! // Let's write some information about a peer. //! // Let's write some information about a peer.
//! { //! {
//! // `peer_or_create` mutably borrows the peerstore, so we have to do it in a local scope. //! // `peer_or_create` mutably borrows the peerstore, so we have to do it in a local scope.
@ -57,7 +57,7 @@
//! peer.add_addr("/ip4/10.11.12.13/tcp/20000".parse::<Multiaddr>().unwrap(), //! peer.add_addr("/ip4/10.11.12.13/tcp/20000".parse::<Multiaddr>().unwrap(),
//! Duration::from_millis(5000)); //! Duration::from_millis(5000));
//! } //! }
//! //!
//! // Now let's load back the info. //! // Now let's load back the info.
//! { //! {
//! let mut peer = peerstore.peer(&peer_id).expect("peer doesn't exist in the peerstore"); //! let mut peer = peerstore.peer(&peer_id).expect("peer doesn't exist in the peerstore");
@ -80,7 +80,7 @@ extern crate serde_derive;
use std::fmt; use std::fmt;
use base58::ToBase58; use base58::ToBase58;
pub use self::peerstore::{Peerstore, PeerAccess}; pub use self::peerstore::{PeerAccess, Peerstore};
#[macro_use] #[macro_use]
mod peerstore_tests; mod peerstore_tests;
@ -141,17 +141,16 @@ impl PeerId {
/// Returns the raw bytes of the hash of this `PeerId`. /// Returns the raw bytes of the hash of this `PeerId`.
#[inline] #[inline]
pub fn hash(&self) -> &[u8] { pub fn hash(&self) -> &[u8] {
let multihash::Multihash { digest, .. } = multihash::decode(&self.multihash) let multihash::Multihash { digest, .. } =
.expect("our inner value should always be valid"); multihash::decode(&self.multihash).expect("our inner value should always be valid");
digest digest
} }
/// Checks whether the public key passed as parameter matches the public key of this `PeerId`. /// Checks whether the public key passed as parameter matches the public key of this `PeerId`.
pub fn is_public_key(&self, public_key: &[u8]) -> bool { pub fn is_public_key(&self, public_key: &[u8]) -> bool {
let multihash::Multihash { alg, .. } = multihash::decode(&self.multihash) let multihash::Multihash { alg, .. } =
.expect("our inner value should always be valid"); multihash::decode(&self.multihash).expect("our inner value should always be valid");
let compare = multihash::encode(alg, public_key) let compare = multihash::encode(alg, public_key).expect("unsupported multihash algorithm"); // TODO: what to do here?
.expect("unsupported multihash algorithm"); // TODO: what to do here?
compare == self.multihash compare == self.multihash
} }
} }

View File

@ -24,8 +24,8 @@ use super::TTL;
use PeerId; use PeerId;
use multiaddr::Multiaddr; use multiaddr::Multiaddr;
use owning_ref::OwningRefMut; use owning_ref::OwningRefMut;
use peer_info::{PeerInfo, AddAddrBehaviour}; use peer_info::{AddAddrBehaviour, PeerInfo};
use peerstore::{Peerstore, PeerAccess}; use peerstore::{PeerAccess, Peerstore};
use std::collections::HashMap; use std::collections::HashMap;
use std::iter; use std::iter;
use std::sync::{Mutex, MutexGuard}; use std::sync::{Mutex, MutexGuard};
@ -34,80 +34,83 @@ use std::vec::IntoIter as VecIntoIter;
/// Implementation of the `Peerstore` trait that simply stores the peer information in memory. /// Implementation of the `Peerstore` trait that simply stores the peer information in memory.
#[derive(Debug)] #[derive(Debug)]
pub struct MemoryPeerstore { pub struct MemoryPeerstore {
store: Mutex<HashMap<PeerId, PeerInfo>>, store: Mutex<HashMap<PeerId, PeerInfo>>,
} }
impl MemoryPeerstore { impl MemoryPeerstore {
/// Initializes a new `MemoryPeerstore`. The database is initially empty. /// Initializes a new `MemoryPeerstore`. The database is initially empty.
#[inline] #[inline]
pub fn empty() -> MemoryPeerstore { pub fn empty() -> MemoryPeerstore {
MemoryPeerstore { store: Mutex::new(HashMap::new()) } MemoryPeerstore {
} store: Mutex::new(HashMap::new()),
}
}
} }
impl Default for MemoryPeerstore { impl Default for MemoryPeerstore {
#[inline] #[inline]
fn default() -> MemoryPeerstore { fn default() -> MemoryPeerstore {
MemoryPeerstore::empty() MemoryPeerstore::empty()
} }
} }
impl<'a> Peerstore for &'a MemoryPeerstore { impl<'a> Peerstore for &'a MemoryPeerstore {
type PeerAccess = MemoryPeerstoreAccess<'a>; type PeerAccess = MemoryPeerstoreAccess<'a>;
type PeersIter = VecIntoIter<PeerId>; type PeersIter = VecIntoIter<PeerId>;
fn peer(self, peer_id: &PeerId) -> Option<Self::PeerAccess> { fn peer(self, peer_id: &PeerId) -> Option<Self::PeerAccess> {
let lock = self.store.lock().unwrap(); let lock = self.store.lock().unwrap();
OwningRefMut::new(lock) OwningRefMut::new(lock)
.try_map_mut(|n| n.get_mut(peer_id).ok_or(())) .try_map_mut(|n| n.get_mut(peer_id).ok_or(()))
.ok() .ok()
.map(MemoryPeerstoreAccess) .map(MemoryPeerstoreAccess)
} }
fn peer_or_create(self, peer_id: &PeerId) -> Self::PeerAccess { fn peer_or_create(self, peer_id: &PeerId) -> Self::PeerAccess {
let lock = self.store.lock().unwrap(); let lock = self.store.lock().unwrap();
let r = OwningRefMut::new(lock) let r = OwningRefMut::new(lock)
.map_mut(|n| n.entry(peer_id.clone()).or_insert_with(|| PeerInfo::new())); .map_mut(|n| n.entry(peer_id.clone()).or_insert_with(|| PeerInfo::new()));
MemoryPeerstoreAccess(r) MemoryPeerstoreAccess(r)
} }
fn peers(self) -> Self::PeersIter { fn peers(self) -> Self::PeersIter {
let lock = self.store.lock().unwrap(); let lock = self.store.lock().unwrap();
lock.keys().cloned().collect::<Vec<_>>().into_iter() lock.keys().cloned().collect::<Vec<_>>().into_iter()
} }
} }
// Note: Rust doesn't provide a `MutexGuard::map` method, otherwise we could directly store a // Note: Rust doesn't provide a `MutexGuard::map` method, otherwise we could directly store a
// `MutexGuard<'a, (&'a PeerId, &'a PeerInfo)>`. // `MutexGuard<'a, (&'a PeerId, &'a PeerInfo)>`.
pub struct MemoryPeerstoreAccess<'a>(OwningRefMut<MutexGuard<'a, HashMap<PeerId, PeerInfo>>, PeerInfo>); pub struct MemoryPeerstoreAccess<'a>(
OwningRefMut<MutexGuard<'a, HashMap<PeerId, PeerInfo>>, PeerInfo>,
);
impl<'a> PeerAccess for MemoryPeerstoreAccess<'a> { impl<'a> PeerAccess for MemoryPeerstoreAccess<'a> {
type AddrsIter = VecIntoIter<Multiaddr>; type AddrsIter = VecIntoIter<Multiaddr>;
#[inline] #[inline]
fn addrs(&self) -> Self::AddrsIter { fn addrs(&self) -> Self::AddrsIter {
self.0.addrs().cloned().collect::<Vec<_>>().into_iter() self.0.addrs().cloned().collect::<Vec<_>>().into_iter()
} }
#[inline] #[inline]
fn add_addr(&mut self, addr: Multiaddr, ttl: TTL) { fn add_addr(&mut self, addr: Multiaddr, ttl: TTL) {
self.0.add_addr(addr, ttl, AddAddrBehaviour::IgnoreTtlIfInferior); self.0
} .add_addr(addr, ttl, AddAddrBehaviour::IgnoreTtlIfInferior);
}
#[inline] #[inline]
fn set_addr_ttl(&mut self, addr: Multiaddr, ttl: TTL) { fn set_addr_ttl(&mut self, addr: Multiaddr, ttl: TTL) {
self.0.add_addr(addr, ttl, AddAddrBehaviour::OverwriteTtl); self.0.add_addr(addr, ttl, AddAddrBehaviour::OverwriteTtl);
} }
#[inline] #[inline]
fn clear_addrs(&mut self) { fn clear_addrs(&mut self) {
self.0.set_addrs(iter::empty()); self.0.set_addrs(iter::empty());
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
peerstore_tests!({ peerstore_tests!({ ::memory_peerstore::MemoryPeerstore::empty() });
::memory_peerstore::MemoryPeerstore::empty()
});
} }

View File

@ -28,7 +28,7 @@
use TTL; use TTL;
use multiaddr::Multiaddr; use multiaddr::Multiaddr;
use serde::{Serialize, Deserialize, Serializer, Deserializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::de::Error as DeserializerError; use serde::de::Error as DeserializerError;
use serde::ser::SerializeStruct; use serde::ser::SerializeStruct;
use std::cmp::Ordering; use std::cmp::Ordering;
@ -37,130 +37,133 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
/// Information about a peer. /// Information about a peer.
#[derive(Debug, Clone, Default, PartialEq, Eq)] #[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct PeerInfo { pub struct PeerInfo {
// Adresses, and the time at which they will be considered expired. // Adresses, and the time at which they will be considered expired.
addrs: Vec<(Multiaddr, SystemTime)>, addrs: Vec<(Multiaddr, SystemTime)>,
} }
impl PeerInfo { impl PeerInfo {
/// Builds a new empty `PeerInfo`. /// Builds a new empty `PeerInfo`.
#[inline] #[inline]
pub fn new() -> PeerInfo { pub fn new() -> PeerInfo {
PeerInfo { addrs: vec![] } PeerInfo { addrs: vec![] }
} }
/// Returns the list of the non-expired addresses stored in this `PeerInfo`. /// Returns the list of the non-expired addresses stored in this `PeerInfo`.
/// ///
/// > **Note**: Keep in mind that this function is racy because addresses can expire between /// > **Note**: Keep in mind that this function is racy because addresses can expire between
/// > the moment when you get them and the moment when you process them. /// > the moment when you get them and the moment when you process them.
// TODO: use -> impl Iterator eventually // TODO: use -> impl Iterator eventually
#[inline] #[inline]
pub fn addrs<'a>(&'a self) -> Box<Iterator<Item = &'a Multiaddr> + 'a> { pub fn addrs<'a>(&'a self) -> Box<Iterator<Item = &'a Multiaddr> + 'a> {
let now = SystemTime::now(); let now = SystemTime::now();
Box::new(self.addrs.iter().filter_map(move |&(ref addr, ref expires)| if *expires >= now { Box::new(self.addrs.iter().filter_map(
Some(addr) move |&(ref addr, ref expires)| if *expires >= now { Some(addr) } else { None },
} else { ))
None }
}))
}
/// Sets the list of addresses and their time-to-live. /// Sets the list of addresses and their time-to-live.
/// ///
/// This removes all previously-stored addresses and replaces them with new ones. /// This removes all previously-stored addresses and replaces them with new ones.
#[inline] #[inline]
pub fn set_addrs<I>(&mut self, addrs: I) pub fn set_addrs<I>(&mut self, addrs: I)
where I: IntoIterator<Item = (Multiaddr, TTL)> where
{ I: IntoIterator<Item = (Multiaddr, TTL)>,
let now = SystemTime::now(); {
self.addrs = addrs.into_iter().map(move |(addr, ttl)| (addr, now + ttl)).collect(); let now = SystemTime::now();
} self.addrs = addrs
.into_iter()
.map(move |(addr, ttl)| (addr, now + ttl))
.collect();
}
/// Adds a single address and its time-to-live. /// Adds a single address and its time-to-live.
/// ///
/// If the peer info already knows about that address, then what happens depends on the /// If the peer info already knows about that address, then what happens depends on the
/// `behaviour` parameter. /// `behaviour` parameter.
pub fn add_addr(&mut self, addr: Multiaddr, ttl: TTL, behaviour: AddAddrBehaviour) { pub fn add_addr(&mut self, addr: Multiaddr, ttl: TTL, behaviour: AddAddrBehaviour) {
let expires = SystemTime::now() + ttl; let expires = SystemTime::now() + ttl;
if let Some(&mut (_, ref mut existing_expires)) = if let Some(&mut (_, ref mut existing_expires)) =
self.addrs.iter_mut().find(|&&mut (ref a, _)| a == &addr) self.addrs.iter_mut().find(|&&mut (ref a, _)| a == &addr)
{ {
if behaviour == AddAddrBehaviour::OverwriteTtl || *existing_expires < expires { if behaviour == AddAddrBehaviour::OverwriteTtl || *existing_expires < expires {
*existing_expires = expires; *existing_expires = expires;
} }
return; return;
} }
self.addrs.push((addr, expires)); self.addrs.push((addr, expires));
} }
} }
/// Behaviour of the `add_addr` function. /// Behaviour of the `add_addr` function.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum AddAddrBehaviour { pub enum AddAddrBehaviour {
/// Always overwrite the existing TTL. /// Always overwrite the existing TTL.
OverwriteTtl, OverwriteTtl,
/// Don't overwrite if the TTL is larger. /// Don't overwrite if the TTL is larger.
IgnoreTtlIfInferior, IgnoreTtlIfInferior,
} }
impl Serialize for PeerInfo { impl Serialize for PeerInfo {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer where
{ S: Serializer,
let mut s = serializer.serialize_struct("PeerInfo", 2)?; {
s.serialize_field( let mut s = serializer.serialize_struct("PeerInfo", 2)?;
"addrs", s.serialize_field(
&self.addrs "addrs",
.iter() &self.addrs
.map(|&(ref addr, ref expires)| { .iter()
let addr = addr.to_bytes(); .map(|&(ref addr, ref expires)| {
let from_epoch = expires.duration_since(UNIX_EPOCH) let addr = addr.to_bytes();
// This `unwrap_or` case happens if the user has their system time set to let from_epoch = expires.duration_since(UNIX_EPOCH)
// before EPOCH. Times-to-live will be be longer than expected, but it's a very // This `unwrap_or` case happens if the user has their system time set to
// improbable corner case and is not attackable in any way, so we don't really // before EPOCH. Times-to-live will be be longer than expected, but it's a very
// care. // improbable corner case and is not attackable in any way, so we don't really
.unwrap_or(Duration::new(0, 0)); // care.
let secs = from_epoch.as_secs() .unwrap_or(Duration::new(0, 0));
.saturating_mul(1_000) let secs = from_epoch
.saturating_add(from_epoch.subsec_nanos() as u64 / 1_000_000); .as_secs()
(addr, secs) .saturating_mul(1_000)
}) .saturating_add(from_epoch.subsec_nanos() as u64 / 1_000_000);
.collect::<Vec<_>>(), (addr, secs)
)?; })
s.end() .collect::<Vec<_>>(),
} )?;
s.end()
}
} }
impl<'de> Deserialize<'de> for PeerInfo { impl<'de> Deserialize<'de> for PeerInfo {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de> where
{ D: Deserializer<'de>,
// We deserialize to an intermdiate struct first, then turn that struct into a `PeerInfo`. {
let interm = { // We deserialize to an intermdiate struct first, then turn that struct into a `PeerInfo`.
#[derive(Deserialize)] let interm = {
struct Interm { #[derive(Deserialize)]
addrs: Vec<(String, u64)>, struct Interm {
} addrs: Vec<(String, u64)>,
Interm::deserialize(deserializer)? }
}; Interm::deserialize(deserializer)?
};
let addrs = { let addrs = {
let mut out = Vec::with_capacity(interm.addrs.len()); let mut out = Vec::with_capacity(interm.addrs.len());
for (addr, since_epoch) in interm.addrs { for (addr, since_epoch) in interm.addrs {
let addr = match addr.parse::<Multiaddr>() { let addr = match addr.parse::<Multiaddr>() {
Ok(a) => a, Ok(a) => a,
Err(err) => return Err(DeserializerError::custom(err)), Err(err) => return Err(DeserializerError::custom(err)),
}; };
let expires = UNIX_EPOCH + Duration::from_millis(since_epoch); let expires = UNIX_EPOCH + Duration::from_millis(since_epoch);
out.push((addr, expires)); out.push((addr, expires));
} }
out out
}; };
Ok(PeerInfo { Ok(PeerInfo { addrs: addrs })
addrs: addrs, }
})
}
} }
// The reason why we need to implement the PartialOrd trait is that the datastore library (a // The reason why we need to implement the PartialOrd trait is that the datastore library (a
@ -169,8 +172,8 @@ impl<'de> Deserialize<'de> for PeerInfo {
// Since the struct that implements PartialOrd is internal and since we never use this ordering // Since the struct that implements PartialOrd is internal and since we never use this ordering
// feature, I think it's ok to have this code. // feature, I think it's ok to have this code.
impl PartialOrd for PeerInfo { impl PartialOrd for PeerInfo {
#[inline] #[inline]
fn partial_cmp(&self, _other: &Self) -> Option<Ordering> { fn partial_cmp(&self, _other: &Self) -> Option<Ordering> {
None None
} }
} }

View File

@ -30,69 +30,71 @@ use multiaddr::Multiaddr;
/// Therefore this trait should likely be implemented on `&'a ConcretePeerstore` instead of /// Therefore this trait should likely be implemented on `&'a ConcretePeerstore` instead of
/// on `ConcretePeerstore`. /// on `ConcretePeerstore`.
pub trait Peerstore { pub trait Peerstore {
/// Grants access to the a peer inside the peer store. /// Grants access to the a peer inside the peer store.
type PeerAccess: PeerAccess; type PeerAccess: PeerAccess;
/// List of the peers in this peer store. /// List of the peers in this peer store.
type PeersIter: Iterator<Item = PeerId>; type PeersIter: Iterator<Item = PeerId>;
/// Grants access to a peer by its ID. /// Grants access to a peer by its ID.
fn peer(self, peer_id: &PeerId) -> Option<Self::PeerAccess>; fn peer(self, peer_id: &PeerId) -> Option<Self::PeerAccess>;
/// Grants access to a peer by its ID or creates it. /// Grants access to a peer by its ID or creates it.
fn peer_or_create(self, peer_id: &PeerId) -> Self::PeerAccess; fn peer_or_create(self, peer_id: &PeerId) -> Self::PeerAccess;
/// Returns a list of peers in this peer store. /// Returns a list of peers in this peer store.
/// ///
/// Keep in mind that the trait implementation may allow new peers to be added or removed at /// Keep in mind that the trait implementation may allow new peers to be added or removed at
/// any time. If that is the case, you have to take into account that this is only an /// any time. If that is the case, you have to take into account that this is only an
/// indication. /// indication.
fn peers(self) -> Self::PeersIter; fn peers(self) -> Self::PeersIter;
} }
/// Implemented on objects that represent an open access to a peer stored in a peer store. /// Implemented on objects that represent an open access to a peer stored in a peer store.
/// ///
/// The exact semantics of "open access" depend on the trait implementation. /// The exact semantics of "open access" depend on the trait implementation.
pub trait PeerAccess { pub trait PeerAccess {
/// Iterator returned by `addrs`. /// Iterator returned by `addrs`.
type AddrsIter: Iterator<Item = Multiaddr>; type AddrsIter: Iterator<Item = Multiaddr>;
/// Returns all known and non-expired addresses for a given peer. /// Returns all known and non-expired addresses for a given peer.
/// ///
/// > **Note**: Keep in mind that this function is racy because addresses can expire between /// > **Note**: Keep in mind that this function is racy because addresses can expire between
/// > the moment when you get them and the moment when you process them. /// > the moment when you get them and the moment when you process them.
fn addrs(&self) -> Self::AddrsIter; fn addrs(&self) -> Self::AddrsIter;
/// Adds an address to a peer. /// Adds an address to a peer.
/// ///
/// If the manager already has this address stored and with a longer TTL, then the operation /// If the manager already has this address stored and with a longer TTL, then the operation
/// is a no-op. /// is a no-op.
fn add_addr(&mut self, addr: Multiaddr, ttl: TTL); fn add_addr(&mut self, addr: Multiaddr, ttl: TTL);
// Similar to calling `add_addr` multiple times in a row. // Similar to calling `add_addr` multiple times in a row.
#[inline] #[inline]
fn add_addrs<I>(&mut self, addrs: I, ttl: TTL) fn add_addrs<I>(&mut self, addrs: I, ttl: TTL)
where I: IntoIterator<Item = Multiaddr> where
{ I: IntoIterator<Item = Multiaddr>,
for addr in addrs.into_iter() { {
self.add_addr(addr, ttl); for addr in addrs.into_iter() {
} self.add_addr(addr, ttl);
} }
}
/// Sets the TTL of an address of a peer. Adds the address if it is currently unknown. /// Sets the TTL of an address of a peer. Adds the address if it is currently unknown.
/// ///
/// Contrary to `add_addr`, this operation is never a no-op. /// Contrary to `add_addr`, this operation is never a no-op.
#[inline] #[inline]
fn set_addr_ttl(&mut self, addr: Multiaddr, ttl: TTL); fn set_addr_ttl(&mut self, addr: Multiaddr, ttl: TTL);
// Similar to calling `set_addr_ttl` multiple times in a row. // Similar to calling `set_addr_ttl` multiple times in a row.
fn set_addrs_ttl<I>(&mut self, addrs: I, ttl: TTL) fn set_addrs_ttl<I>(&mut self, addrs: I, ttl: TTL)
where I: IntoIterator<Item = Multiaddr> where
{ I: IntoIterator<Item = Multiaddr>,
for addr in addrs.into_iter() { {
self.add_addr(addr, ttl); for addr in addrs.into_iter() {
} self.add_addr(addr, ttl);
} }
}
/// Removes all previously stored addresses. /// Removes all previously stored addresses.
fn clear_addrs(&mut self); fn clear_addrs(&mut self);
} }

View File

@ -79,7 +79,8 @@ macro_rules! peerstore_tests {
let peer_id = PeerId::from_public_key(&[1, 2, 3]); let peer_id = PeerId::from_public_key(&[1, 2, 3]);
let addr = "/ip4/0.0.0.0/tcp/0".parse::<Multiaddr>().unwrap(); let addr = "/ip4/0.0.0.0/tcp/0".parse::<Multiaddr>().unwrap();
peer_store.peer_or_create(&peer_id).add_addr(addr.clone(), Duration::from_millis(5000)); peer_store.peer_or_create(&peer_id)
.add_addr(addr.clone(), Duration::from_millis(5000));
peer_store.peer(&peer_id).unwrap().clear_addrs(); peer_store.peer(&peer_id).unwrap().clear_addrs();
let addrs = peer_store.peer(&peer_id).unwrap().addrs(); let addrs = peer_store.peer(&peer_id).unwrap().addrs();
@ -95,8 +96,10 @@ macro_rules! peerstore_tests {
let addr1 = "/ip4/0.0.0.0/tcp/0".parse::<Multiaddr>().unwrap(); let addr1 = "/ip4/0.0.0.0/tcp/0".parse::<Multiaddr>().unwrap();
let addr2 = "/ip4/0.0.0.1/tcp/0".parse::<Multiaddr>().unwrap(); let addr2 = "/ip4/0.0.0.1/tcp/0".parse::<Multiaddr>().unwrap();
peer_store.peer_or_create(&peer_id).add_addr(addr1.clone(), Duration::from_millis(5000)); peer_store.peer_or_create(&peer_id)
peer_store.peer_or_create(&peer_id).add_addr(addr2.clone(), Duration::from_millis(5000)); .add_addr(addr1.clone(), Duration::from_millis(5000));
peer_store.peer_or_create(&peer_id)
.add_addr(addr2.clone(), Duration::from_millis(5000));
assert_eq!(peer_store.peer(&peer_id).unwrap().addrs().count(), 2); assert_eq!(peer_store.peer(&peer_id).unwrap().addrs().count(), 2);
// `add_addr` must not overwrite the TTL because it's already higher // `add_addr` must not overwrite the TTL because it's already higher
@ -114,11 +117,14 @@ macro_rules! peerstore_tests {
let addr1 = "/ip4/0.0.0.0/tcp/0".parse::<Multiaddr>().unwrap(); let addr1 = "/ip4/0.0.0.0/tcp/0".parse::<Multiaddr>().unwrap();
let addr2 = "/ip4/0.0.0.1/tcp/0".parse::<Multiaddr>().unwrap(); let addr2 = "/ip4/0.0.0.1/tcp/0".parse::<Multiaddr>().unwrap();
peer_store.peer_or_create(&peer_id).add_addr(addr1.clone(), Duration::from_millis(5000)); peer_store.peer_or_create(&peer_id)
peer_store.peer_or_create(&peer_id).add_addr(addr2.clone(), Duration::from_millis(5000)); .add_addr(addr1.clone(), Duration::from_millis(5000));
peer_store.peer_or_create(&peer_id)
.add_addr(addr2.clone(), Duration::from_millis(5000));
assert_eq!(peer_store.peer(&peer_id).unwrap().addrs().count(), 2); assert_eq!(peer_store.peer(&peer_id).unwrap().addrs().count(), 2);
peer_store.peer_or_create(&peer_id).set_addr_ttl(addr1.clone(), Duration::from_millis(0)); peer_store.peer_or_create(&peer_id)
.set_addr_ttl(addr1.clone(), Duration::from_millis(0));
thread::sleep(Duration::from_millis(2)); thread::sleep(Duration::from_millis(2));
assert_eq!(peer_store.peer(&peer_id).unwrap().addrs().count(), 1); assert_eq!(peer_store.peer(&peer_id).unwrap().addrs().count(), 1);
} }

View File

@ -57,21 +57,21 @@
//! extern crate libp2p_swarm; //! extern crate libp2p_swarm;
//! extern crate libp2p_tcp_transport; //! extern crate libp2p_tcp_transport;
//! extern crate tokio_core; //! extern crate tokio_core;
//! //!
//! use futures::Future; //! use futures::Future;
//! use libp2p_ping::Ping; //! use libp2p_ping::Ping;
//! use libp2p_swarm::Transport; //! use libp2p_swarm::Transport;
//! //!
//! # fn main() { //! # fn main() {
//! let mut core = tokio_core::reactor::Core::new().unwrap(); //! let mut core = tokio_core::reactor::Core::new().unwrap();
//! //!
//! let ping_finished_future = libp2p_tcp_transport::TcpConfig::new(core.handle()) //! let ping_finished_future = libp2p_tcp_transport::TcpConfig::new(core.handle())
//! .with_upgrade(Ping) //! .with_upgrade(Ping)
//! .dial("127.0.0.1:12345".parse::<libp2p_swarm::Multiaddr>().unwrap()).unwrap_or_else(|_| panic!()) //! .dial("127.0.0.1:12345".parse::<libp2p_swarm::Multiaddr>().unwrap()).unwrap_or_else(|_| panic!())
//! .and_then(|((mut pinger, service), _)| { //! .and_then(|((mut pinger, service), _)| {
//! pinger.ping().map_err(|_| panic!()).select(service).map_err(|_| panic!()) //! pinger.ping().map_err(|_| panic!()).select(service).map_err(|_| panic!())
//! }); //! });
//! //!
//! // Runs until the ping arrives. //! // Runs until the ping arrives.
//! core.run(ping_finished_future).unwrap(); //! core.run(ping_finished_future).unwrap();
//! # } //! # }
@ -88,9 +88,9 @@ extern crate parking_lot;
extern crate rand; extern crate rand;
extern crate tokio_io; extern crate tokio_io;
use bytes::{Bytes, BytesMut, BufMut}; use bytes::{BufMut, Bytes, BytesMut};
use futures::{Future, Sink, Stream}; use futures::{Future, Sink, Stream};
use futures::future::{FutureResult, IntoFuture, loop_fn, Loop}; use futures::future::{loop_fn, FutureResult, IntoFuture, Loop};
use futures::sync::{mpsc, oneshot}; use futures::sync::{mpsc, oneshot};
use libp2p_swarm::Multiaddr; use libp2p_swarm::Multiaddr;
use libp2p_swarm::transport::{ConnectionUpgrade, Endpoint}; use libp2p_swarm::transport::{ConnectionUpgrade, Endpoint};
@ -104,7 +104,7 @@ use std::io::Error as IoError;
use std::iter; use std::iter;
use std::sync::Arc; use std::sync::Arc;
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::codec::{Encoder, Decoder}; use tokio_io::codec::{Decoder, Encoder};
/// Represents a prototype for an upgrade to handle the ping protocol. /// Represents a prototype for an upgrade to handle the ping protocol.
/// ///
@ -114,136 +114,152 @@ use tokio_io::codec::{Encoder, Decoder};
pub struct Ping; pub struct Ping;
impl<C> ConnectionUpgrade<C> for Ping impl<C> ConnectionUpgrade<C> for Ping
where C: AsyncRead + AsyncWrite + 'static where
C: AsyncRead + AsyncWrite + 'static,
{ {
type NamesIter = iter::Once<(Bytes, Self::UpgradeIdentifier)>; type NamesIter = iter::Once<(Bytes, Self::UpgradeIdentifier)>;
type UpgradeIdentifier = (); type UpgradeIdentifier = ();
#[inline] #[inline]
fn protocol_names(&self) -> Self::NamesIter { fn protocol_names(&self) -> Self::NamesIter {
iter::once(("/ipfs/ping/1.0.0".into(), ())) iter::once(("/ipfs/ping/1.0.0".into(), ()))
} }
type Output = (Pinger, Box<Future<Item = (), Error = IoError>>); type Output = (Pinger, Box<Future<Item = (), Error = IoError>>);
type Future = FutureResult<Self::Output, IoError>; type Future = FutureResult<Self::Output, IoError>;
#[inline] #[inline]
fn upgrade(self, socket: C, _: Self::UpgradeIdentifier, _: Endpoint, remote_addr: &Multiaddr) fn upgrade(
-> Self::Future self,
{ socket: C,
// # How does it work? _: Self::UpgradeIdentifier,
// _: Endpoint,
// All the actual processing is performed by the *ponger*. remote_addr: &Multiaddr,
// We use a channel in order to send ping requests from the pinger to the ponger. ) -> Self::Future {
// # How does it work?
//
// All the actual processing is performed by the *ponger*.
// We use a channel in order to send ping requests from the pinger to the ponger.
let (tx, rx) = mpsc::channel(8); let (tx, rx) = mpsc::channel(8);
// Ignore the errors if `tx` closed. `tx` is only ever closed if the ponger is closed, // Ignore the errors if `tx` closed. `tx` is only ever closed if the ponger is closed,
// which means that the connection to the remote is closed. Therefore we make the `rx` // which means that the connection to the remote is closed. Therefore we make the `rx`
// never produce anything. // never produce anything.
let rx = rx.then(|r| Ok(r.ok())).filter_map(|a| a); let rx = rx.then(|r| Ok(r.ok())).filter_map(|a| a);
let os_rng = match OsRng::new() { let os_rng = match OsRng::new() {
Ok(r) => r, Ok(r) => r,
Err(err) => return Err(err).into_future(), Err(err) => return Err(err).into_future(),
}; };
let pinger = Pinger { let pinger = Pinger {
send: tx, send: tx,
os_rng: os_rng, os_rng: os_rng,
}; };
// Hashmap that associates outgoing payloads to one-shot senders. // Hashmap that associates outgoing payloads to one-shot senders.
// TODO: can't figure out how to make it work without using an Arc/Mutex // TODO: can't figure out how to make it work without using an Arc/Mutex
let expected_pongs = Arc::new(Mutex::new(HashMap::with_capacity(4))); let expected_pongs = Arc::new(Mutex::new(HashMap::with_capacity(4)));
let sink_stream = socket.framed(Codec).map(|msg| Message::Received(msg.freeze())); let sink_stream = socket
let (sink, stream) = sink_stream.split(); .framed(Codec)
.map(|msg| Message::Received(msg.freeze()));
let (sink, stream) = sink_stream.split();
let remote_addr = if log_enabled!(target: "libp2p-ping", Level::Debug) { let remote_addr = if log_enabled!(target: "libp2p-ping", Level::Debug) {
Some(remote_addr.clone()) Some(remote_addr.clone())
} else { } else {
None None
}; };
let future = loop_fn((sink, stream.select(rx)), move |(sink, stream)| { let future = loop_fn((sink, stream.select(rx)), move |(sink, stream)| {
let expected_pongs = expected_pongs.clone(); let expected_pongs = expected_pongs.clone();
let remote_addr = remote_addr.clone(); let remote_addr = remote_addr.clone();
stream.into_future().map_err(|(err, _)| err).and_then(move |(message, stream)| { stream
let mut expected_pongs = expected_pongs.lock(); .into_future()
.map_err(|(err, _)| err)
.and_then(move |(message, stream)| {
let mut expected_pongs = expected_pongs.lock();
if let Some(message) = message { if let Some(message) = message {
match message { match message {
Message::Ping(payload, finished) => { Message::Ping(payload, finished) => {
// Ping requested by the user through the `Pinger`. // Ping requested by the user through the `Pinger`.
debug!(target: "libp2p-ping", "Sending ping to {:?} with payload {:?}", debug!(target: "libp2p-ping", "Sending ping to {:?} with payload {:?}",
remote_addr.expect("debug log level is enabled"), payload); remote_addr.expect("debug log level is enabled"), payload);
expected_pongs.insert(payload.clone(), finished); expected_pongs.insert(payload.clone(), finished);
Box::new( Box::new(
sink.send(payload).map(|sink| Loop::Continue((sink, stream))), sink.send(payload)
) as Box<Future<Item = _, Error = _>> .map(|sink| Loop::Continue((sink, stream))),
} )
Message::Received(payload) => { as Box<Future<Item = _, Error = _>>
// Received a payload from the remote. }
if let Some(fut) = expected_pongs.remove(&payload) { Message::Received(payload) => {
// Payload was ours. Signalling future. // Received a payload from the remote.
// Errors can happen if the user closed the receiving end of if let Some(fut) = expected_pongs.remove(&payload) {
// the future, which is fine to ignore. // Payload was ours. Signalling future.
debug!(target: "libp2p-ping", "Received pong from {:?} \ // Errors can happen if the user closed the receiving end of
(payload={:?}) ; ping fufilled", // the future, which is fine to ignore.
remote_addr.expect("debug log level is enabled"), payload); debug!(target: "libp2p-ping", "Received pong from {:?} \
let _ = fut.send(()); (payload={:?}) ; ping fufilled",
Box::new(Ok(Loop::Continue((sink, stream))).into_future()) as remote_addr.expect("debug log level is enabled"), payload);
Box<Future<Item = _, Error = _>> let _ = fut.send(());
} else { Box::new(Ok(Loop::Continue((sink, stream))).into_future())
// Payload was not ours. Sending it back. as Box<Future<Item = _, Error = _>>
debug!(target: "libp2p-ping", "Received ping from {:?} \ } else {
(payload={:?}) ; sending back", // Payload was not ours. Sending it back.
remote_addr.expect("debug log level is enabled"), payload); debug!(target: "libp2p-ping", "Received ping from {:?} \
Box::new( (payload={:?}) ; sending back",
sink.send(payload).map(|sink| Loop::Continue((sink, stream))), remote_addr.expect("debug log level is enabled"), payload);
) as Box<Future<Item = _, Error = _>> Box::new(
} sink.send(payload)
} .map(|sink| Loop::Continue((sink, stream))),
} )
as Box<Future<Item = _, Error = _>>
}
}
}
} else {
Box::new(Ok(Loop::Break(())).into_future())
as Box<Future<Item = _, Error = _>>
}
})
});
} else { Ok((pinger, Box::new(future) as Box<_>)).into_future()
Box::new(Ok(Loop::Break(())).into_future()) as Box<Future<Item = _, Error = _>> }
}
})
});
Ok((pinger, Box::new(future) as Box<_>)).into_future()
}
} }
/// Controller for the ping service. Makes it possible to send pings to the remote. /// Controller for the ping service. Makes it possible to send pings to the remote.
pub struct Pinger { pub struct Pinger {
send: mpsc::Sender<Message>, send: mpsc::Sender<Message>,
os_rng: OsRng, os_rng: OsRng,
} }
impl Pinger { impl Pinger {
/// Sends a ping. Returns a future that is signaled when a pong is received. /// Sends a ping. Returns a future that is signaled when a pong is received.
/// ///
/// **Note**: Please be aware that there is no timeout on the ping. You should handle the /// **Note**: Please be aware that there is no timeout on the ping. You should handle the
/// timeout yourself when you call this function. /// timeout yourself when you call this function.
pub fn ping(&mut self) -> Box<Future<Item = (), Error = Box<Error + Send + Sync>>> { pub fn ping(&mut self) -> Box<Future<Item = (), Error = Box<Error + Send + Sync>>> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
let payload: [u8; 32] = Rand::rand(&mut self.os_rng); let payload: [u8; 32] = Rand::rand(&mut self.os_rng);
debug!(target: "libp2p-ping", "Preparing for ping with payload {:?}", payload); debug!(target: "libp2p-ping", "Preparing for ping with payload {:?}", payload);
// Ignore errors if the ponger has been already destroyed. The returned future will never // Ignore errors if the ponger has been already destroyed. The returned future will never
// be signalled. // be signalled.
let fut = self.send.clone().send(Message::Ping(Bytes::from(payload.to_vec()), tx)) let fut = self.send
.from_err() .clone()
.and_then(|_| rx.from_err()); .send(Message::Ping(Bytes::from(payload.to_vec()), tx))
Box::new(fut) as Box<_> .from_err()
} .and_then(|_| rx.from_err());
Box::new(fut) as Box<_>
}
} }
enum Message { enum Message {
Ping(Bytes, oneshot::Sender<()>), Ping(Bytes, oneshot::Sender<()>),
Received(Bytes), Received(Bytes),
} }
// Implementation of the `Codec` trait of tokio-io. Splits frames into groups of 32 bytes. // Implementation of the `Codec` trait of tokio-io. Splits frames into groups of 32 bytes.
@ -251,102 +267,137 @@ enum Message {
struct Codec; struct Codec;
impl Decoder for Codec { impl Decoder for Codec {
type Item = BytesMut; type Item = BytesMut;
type Error = IoError; type Error = IoError;
#[inline] #[inline]
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<BytesMut>, IoError> { fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<BytesMut>, IoError> {
if buf.len() >= 32 { Ok(Some(buf.split_to(32))) } else { Ok(None) } if buf.len() >= 32 {
} Ok(Some(buf.split_to(32)))
} else {
Ok(None)
}
}
} }
impl Encoder for Codec { impl Encoder for Codec {
type Item = Bytes; type Item = Bytes;
type Error = IoError; type Error = IoError;
#[inline] #[inline]
fn encode(&mut self, mut data: Bytes, buf: &mut BytesMut) -> Result<(), IoError> { fn encode(&mut self, mut data: Bytes, buf: &mut BytesMut) -> Result<(), IoError> {
if data.len() != 0 { if data.len() != 0 {
let split = 32 * (1 + ((data.len() - 1) / 32)); let split = 32 * (1 + ((data.len() - 1) / 32));
buf.put(data.split_to(split)); buf.put(data.split_to(split));
} }
Ok(()) Ok(())
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate tokio_core; extern crate tokio_core;
use self::tokio_core::net::TcpListener; use self::tokio_core::net::TcpListener;
use self::tokio_core::net::TcpStream; use self::tokio_core::net::TcpStream;
use self::tokio_core::reactor::Core; use self::tokio_core::reactor::Core;
use super::Ping; use super::Ping;
use futures::future::join_all; use futures::future::join_all;
use futures::Future; use futures::Future;
use futures::Stream; use futures::Stream;
use libp2p_swarm::transport::{ConnectionUpgrade, Endpoint}; use libp2p_swarm::transport::{ConnectionUpgrade, Endpoint};
#[test] #[test]
fn ping_pong() { fn ping_pong() {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap(); let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
let listener_addr = listener.local_addr().unwrap(); let listener_addr = listener.local_addr().unwrap();
let server = listener.incoming() let server = listener
.into_future() .incoming()
.map_err(|(e, _)| e.into()) .into_future()
.and_then(|(c, _)| { .map_err(|(e, _)| e.into())
Ping.upgrade(c.unwrap().0, (), Endpoint::Listener, .and_then(|(c, _)| {
&"/ip4/127.0.0.1/tcp/10000".parse().unwrap()) Ping.upgrade(
}) c.unwrap().0,
.and_then(|(mut pinger, service)| { (),
pinger.ping().map_err(|_| panic!()).select(service).map_err(|_| panic!()) Endpoint::Listener,
}); &"/ip4/127.0.0.1/tcp/10000".parse().unwrap(),
)
})
.and_then(|(mut pinger, service)| {
pinger
.ping()
.map_err(|_| panic!())
.select(service)
.map_err(|_| panic!())
});
let client = TcpStream::connect(&listener_addr, &core.handle()) let client = TcpStream::connect(&listener_addr, &core.handle())
.map_err(|e| e.into()) .map_err(|e| e.into())
.and_then(|c| { .and_then(|c| {
Ping.upgrade(c, (), Endpoint::Dialer, &"/ip4/127.0.0.1/tcp/10000".parse().unwrap()) Ping.upgrade(
}) c,
.and_then(|(mut pinger, service)| { (),
pinger.ping().map_err(|_| panic!()).select(service).map_err(|_| panic!()) Endpoint::Dialer,
}); &"/ip4/127.0.0.1/tcp/10000".parse().unwrap(),
)
})
.and_then(|(mut pinger, service)| {
pinger
.ping()
.map_err(|_| panic!())
.select(service)
.map_err(|_| panic!())
});
core.run(server.join(client)).unwrap(); core.run(server.join(client)).unwrap();
} }
#[test] #[test]
fn multipings() { fn multipings() {
// Check that we can send multiple pings in a row and it will still work. // Check that we can send multiple pings in a row and it will still work.
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap(); let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
let listener_addr = listener.local_addr().unwrap(); let listener_addr = listener.local_addr().unwrap();
let server = listener.incoming() let server = listener
.into_future() .incoming()
.map_err(|(e, _)| e.into()) .into_future()
.and_then(|(c, _)| { .map_err(|(e, _)| e.into())
Ping.upgrade(c.unwrap().0, (), Endpoint::Listener, .and_then(|(c, _)| {
&"/ip4/127.0.0.1/tcp/10000".parse().unwrap()) Ping.upgrade(
}) c.unwrap().0,
.and_then(|(_, service)| service.map_err(|_| panic!())); (),
Endpoint::Listener,
&"/ip4/127.0.0.1/tcp/10000".parse().unwrap(),
)
})
.and_then(|(_, service)| service.map_err(|_| panic!()));
let client = TcpStream::connect(&listener_addr, &core.handle()) let client = TcpStream::connect(&listener_addr, &core.handle())
.map_err(|e| e.into()) .map_err(|e| e.into())
.and_then(|c| Ping.upgrade(c, (), Endpoint::Dialer, .and_then(|c| {
&"/ip4/127.0.0.1/tcp/1000".parse().unwrap())) Ping.upgrade(
.and_then(|(mut pinger, service)| { c,
let pings = (0 .. 20).map(move |_| { (),
pinger.ping().map_err(|_| ()) Endpoint::Dialer,
}); &"/ip4/127.0.0.1/tcp/1000".parse().unwrap(),
)
})
.and_then(|(mut pinger, service)| {
let pings = (0..20).map(move |_| pinger.ping().map_err(|_| ()));
join_all(pings).map(|_| ()).map_err(|_| panic!()) join_all(pings)
.select(service).map(|_| ()).map_err(|_| panic!()) .map(|_| ())
}); .map_err(|_| panic!())
.select(service)
.map(|_| ())
.map_err(|_| panic!())
});
core.run(server.select(client)).unwrap_or_else(|_| panic!()); core.run(server.select(client)).unwrap_or_else(|_| panic!());
} }
} }

View File

@ -24,72 +24,72 @@
//! helps you with. //! helps you with.
macro_rules! supported_impl { macro_rules! supported_impl {
($mod_name:ident: $ty:ty, $($name:expr => $val:expr),*,) => ( ($mod_name:ident: $ty:ty, $($name:expr => $val:expr),*,) => (
pub mod $mod_name { pub mod $mod_name {
use std::cmp::Ordering; use std::cmp::Ordering;
#[allow(unused_imports)] #[allow(unused_imports)]
use crypto::aes::KeySize; use crypto::aes::KeySize;
#[allow(unused_imports)] #[allow(unused_imports)]
use ring::{agreement, digest}; use ring::{agreement, digest};
use error::SecioError; use error::SecioError;
/// String to advertise to the remote. /// String to advertise to the remote.
pub const PROPOSITION_STRING: &'static str = concat_comma!($($name),*); pub const PROPOSITION_STRING: &'static str = concat_comma!($($name),*);
/// Choose which algorithm to use based on the remote's advertised list. /// Choose which algorithm to use based on the remote's advertised list.
pub fn select_best(hashes_ordering: Ordering, input: &str) -> Result<$ty, SecioError> { pub fn select_best(hashes_ordering: Ordering, input: &str) -> Result<$ty, SecioError> {
match hashes_ordering { match hashes_ordering {
Ordering::Less | Ordering::Equal => { Ordering::Less | Ordering::Equal => {
for second_elem in input.split(',') { for second_elem in input.split(',') {
$( $(
if $name == second_elem { if $name == second_elem {
return Ok($val); return Ok($val);
} }
)+ )+
} }
}, },
Ordering::Greater => { Ordering::Greater => {
$( $(
for second_elem in input.split(',') { for second_elem in input.split(',') {
if $name == second_elem { if $name == second_elem {
return Ok($val); return Ok($val);
} }
} }
)+ )+
}, },
}; };
Err(SecioError::NoSupportIntersection(PROPOSITION_STRING, input.to_owned())) Err(SecioError::NoSupportIntersection(PROPOSITION_STRING, input.to_owned()))
} }
} }
); );
} }
// Concatenates several strings with commas. // Concatenates several strings with commas.
macro_rules! concat_comma { macro_rules! concat_comma {
($first:expr, $($rest:expr),*) => ( ($first:expr, $($rest:expr),*) => (
concat!($first $(, ',', $rest)*) concat!($first $(, ',', $rest)*)
); );
} }
// TODO: there's no library in the Rust ecosystem that supports P-521, but the Go & JS // TODO: there's no library in the Rust ecosystem that supports P-521, but the Go & JS
// implementations advertise it // implementations advertise it
supported_impl!( supported_impl!(
exchanges: &'static agreement::Algorithm, exchanges: &'static agreement::Algorithm,
"P-256" => &agreement::ECDH_P256, "P-256" => &agreement::ECDH_P256,
"P-384" => &agreement::ECDH_P384, "P-384" => &agreement::ECDH_P384,
); );
// TODO: the Go & JS implementations advertise Blowfish ; however doing so in Rust leads to // TODO: the Go & JS implementations advertise Blowfish ; however doing so in Rust leads to
// runtime errors // runtime errors
supported_impl!( supported_impl!(
ciphers: KeySize, ciphers: KeySize,
"AES-128" => KeySize::KeySize128, "AES-128" => KeySize::KeySize128,
"AES-256" => KeySize::KeySize256, "AES-256" => KeySize::KeySize256,
); );
supported_impl!( supported_impl!(
hashes: &'static digest::Algorithm, hashes: &'static digest::Algorithm,
"SHA256" => &digest::SHA256, "SHA256" => &digest::SHA256,
"SHA512" => &digest::SHA512, "SHA512" => &digest::SHA512,
); );

View File

@ -40,78 +40,81 @@ use ring::hmac;
/// ///
/// Also implements `Sink` for convenience. /// Also implements `Sink` for convenience.
pub struct DecoderMiddleware<S> { pub struct DecoderMiddleware<S> {
cipher_state: Box<SynchronousStreamCipher>, cipher_state: Box<SynchronousStreamCipher>,
hmac_key: hmac::VerificationKey, hmac_key: hmac::VerificationKey,
raw_stream: S, raw_stream: S,
} }
impl<S> DecoderMiddleware<S> { impl<S> DecoderMiddleware<S> {
#[inline] #[inline]
pub fn new( pub fn new(
raw_stream: S, raw_stream: S,
cipher: Box<SynchronousStreamCipher>, cipher: Box<SynchronousStreamCipher>,
hmac_key: hmac::VerificationKey, hmac_key: hmac::VerificationKey,
) -> DecoderMiddleware<S> { ) -> DecoderMiddleware<S> {
DecoderMiddleware { DecoderMiddleware {
cipher_state: cipher, cipher_state: cipher,
hmac_key: hmac_key, hmac_key: hmac_key,
raw_stream: raw_stream, raw_stream: raw_stream,
} }
} }
} }
impl<S> Stream for DecoderMiddleware<S> impl<S> Stream for DecoderMiddleware<S>
where S: Stream<Item = BytesMut>, where
S::Error: Into<SecioError> S: Stream<Item = BytesMut>,
S::Error: Into<SecioError>,
{ {
type Item = Vec<u8>; type Item = Vec<u8>;
type Error = SecioError; type Error = SecioError;
#[inline] #[inline]
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
let frame = match self.raw_stream.poll() { let frame = match self.raw_stream.poll() {
Ok(Async::Ready(Some(t))) => t, Ok(Async::Ready(Some(t))) => t,
Ok(Async::Ready(None)) => return Ok(Async::Ready(None)), Ok(Async::Ready(None)) => return Ok(Async::Ready(None)),
Ok(Async::NotReady) => return Ok(Async::NotReady), Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(err) => return Err(err.into()), Err(err) => return Err(err.into()),
}; };
let hmac_num_bytes = self.hmac_key.digest_algorithm().output_len; let hmac_num_bytes = self.hmac_key.digest_algorithm().output_len;
if frame.len() < hmac_num_bytes { if frame.len() < hmac_num_bytes {
debug!(target: "libp2p-secio", "frame too short when decoding secio frame"); debug!(target: "libp2p-secio", "frame too short when decoding secio frame");
return Err(SecioError::FrameTooShort); return Err(SecioError::FrameTooShort);
} }
let (crypted_data, expected_hash) = frame.split_at(frame.len() - hmac_num_bytes); let (crypted_data, expected_hash) = frame.split_at(frame.len() - hmac_num_bytes);
debug_assert_eq!(expected_hash.len(), hmac_num_bytes); debug_assert_eq!(expected_hash.len(), hmac_num_bytes);
if let Err(_) = hmac::verify(&self.hmac_key, crypted_data, expected_hash) { if let Err(_) = hmac::verify(&self.hmac_key, crypted_data, expected_hash) {
debug!(target: "libp2p-secio", "hmac mismatch when decoding secio frame"); debug!(target: "libp2p-secio", "hmac mismatch when decoding secio frame");
return Err(SecioError::HmacNotMatching); return Err(SecioError::HmacNotMatching);
} }
// Note that there is no way to decipher in place with rust-crypto right now. // Note that there is no way to decipher in place with rust-crypto right now.
let mut decrypted_data = crypted_data.to_vec(); let mut decrypted_data = crypted_data.to_vec();
self.cipher_state.process(&crypted_data, &mut decrypted_data); self.cipher_state
.process(&crypted_data, &mut decrypted_data);
Ok(Async::Ready(Some(decrypted_data))) Ok(Async::Ready(Some(decrypted_data)))
} }
} }
impl<S> Sink for DecoderMiddleware<S> impl<S> Sink for DecoderMiddleware<S>
where S: Sink where
S: Sink,
{ {
type SinkItem = S::SinkItem; type SinkItem = S::SinkItem;
type SinkError = S::SinkError; type SinkError = S::SinkError;
#[inline] #[inline]
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> { fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
self.raw_stream.start_send(item) self.raw_stream.start_send(item)
} }
#[inline] #[inline]
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
self.raw_stream.poll_complete() self.raw_stream.poll_complete()
} }
} }

View File

@ -36,63 +36,65 @@ use ring::hmac;
/// ///
/// Also implements `Stream` for convenience. /// Also implements `Stream` for convenience.
pub struct EncoderMiddleware<S> { pub struct EncoderMiddleware<S> {
cipher_state: Box<SynchronousStreamCipher>, cipher_state: Box<SynchronousStreamCipher>,
hmac_key: hmac::SigningKey, hmac_key: hmac::SigningKey,
raw_sink: S, raw_sink: S,
} }
impl<S> EncoderMiddleware<S> { impl<S> EncoderMiddleware<S> {
pub fn new( pub fn new(
raw_sink: S, raw_sink: S,
cipher: Box<SynchronousStreamCipher>, cipher: Box<SynchronousStreamCipher>,
hmac_key: hmac::SigningKey, hmac_key: hmac::SigningKey,
) -> EncoderMiddleware<S> { ) -> EncoderMiddleware<S> {
EncoderMiddleware { EncoderMiddleware {
cipher_state: cipher, cipher_state: cipher,
hmac_key: hmac_key, hmac_key: hmac_key,
raw_sink: raw_sink, raw_sink: raw_sink,
} }
} }
} }
impl<S> Sink for EncoderMiddleware<S> impl<S> Sink for EncoderMiddleware<S>
where S: Sink<SinkItem = BytesMut> where
S: Sink<SinkItem = BytesMut>,
{ {
type SinkItem = BytesMut; type SinkItem = BytesMut;
type SinkError = S::SinkError; type SinkError = S::SinkError;
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> { fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
let capacity = item.len() + self.hmac_key.digest_algorithm().output_len; let capacity = item.len() + self.hmac_key.digest_algorithm().output_len;
// Apparently this is the fastest way of doing. // Apparently this is the fastest way of doing.
// See https://gist.github.com/kirushik/e0d93759b0cd102f814408595c20a9d0 // See https://gist.github.com/kirushik/e0d93759b0cd102f814408595c20a9d0
let mut out_buffer = BytesMut::from(vec![0; capacity]); let mut out_buffer = BytesMut::from(vec![0; capacity]);
{ {
let (out_data, out_sign) = out_buffer.split_at_mut(item.len()); let (out_data, out_sign) = out_buffer.split_at_mut(item.len());
self.cipher_state.process(&item, out_data); self.cipher_state.process(&item, out_data);
let signature = hmac::sign(&self.hmac_key, out_data); let signature = hmac::sign(&self.hmac_key, out_data);
out_sign.copy_from_slice(signature.as_ref()); out_sign.copy_from_slice(signature.as_ref());
} }
self.raw_sink.start_send(out_buffer) self.raw_sink.start_send(out_buffer)
} }
#[inline] #[inline]
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
self.raw_sink.poll_complete() self.raw_sink.poll_complete()
} }
} }
impl<S> Stream for EncoderMiddleware<S> impl<S> Stream for EncoderMiddleware<S>
where S: Stream where
S: Stream,
{ {
type Item = S::Item; type Item = S::Item;
type Error = S::Error; type Error = S::Error;
#[inline] #[inline]
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
self.raw_sink.poll() self.raw_sink.poll()
} }
} }

View File

@ -41,124 +41,148 @@ pub type FullCodec<S> = DecoderMiddleware<EncoderMiddleware<length_delimited::Fr
/// The conversion between the stream/sink items and the socket is done with the given cipher and /// The conversion between the stream/sink items and the socket is done with the given cipher and
/// hash algorithm (which are generally decided during the handshake). /// hash algorithm (which are generally decided during the handshake).
pub fn full_codec<S>( pub fn full_codec<S>(
socket: length_delimited::Framed<S>, socket: length_delimited::Framed<S>,
cipher_encoding: Box<SynchronousStreamCipher>, cipher_encoding: Box<SynchronousStreamCipher>,
encoding_hmac: hmac::SigningKey, encoding_hmac: hmac::SigningKey,
cipher_decoder: Box<SynchronousStreamCipher>, cipher_decoder: Box<SynchronousStreamCipher>,
decoding_hmac: hmac::VerificationKey, decoding_hmac: hmac::VerificationKey,
) -> FullCodec<S> ) -> FullCodec<S>
where S: AsyncRead + AsyncWrite where
S: AsyncRead + AsyncWrite,
{ {
let encoder = EncoderMiddleware::new(socket, cipher_encoding, encoding_hmac); let encoder = EncoderMiddleware::new(socket, cipher_encoding, encoding_hmac);
let codec = DecoderMiddleware::new(encoder, cipher_decoder, decoding_hmac); let codec = DecoderMiddleware::new(encoder, cipher_decoder, decoding_hmac);
codec codec
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate tokio_core; extern crate tokio_core;
use super::DecoderMiddleware; use super::DecoderMiddleware;
use super::EncoderMiddleware; use super::EncoderMiddleware;
use super::full_codec; use super::full_codec;
use bytes::BytesMut; use bytes::BytesMut;
use crypto::aessafe::AesSafe256Encryptor; use crypto::aessafe::AesSafe256Encryptor;
use crypto::blockmodes::CtrMode; use crypto::blockmodes::CtrMode;
use error::SecioError; use error::SecioError;
use futures::{Future, Sink, Stream}; use futures::{Future, Sink, Stream};
use futures::sync::mpsc::channel; use futures::sync::mpsc::channel;
use rand; use rand;
use ring::digest::SHA256; use ring::digest::SHA256;
use ring::hmac::SigningKey; use ring::hmac::SigningKey;
use ring::hmac::VerificationKey; use ring::hmac::VerificationKey;
use std::io::Error as IoError; use std::io::Error as IoError;
use self::tokio_core::net::TcpListener; use self::tokio_core::net::TcpListener;
use self::tokio_core::net::TcpStream; use self::tokio_core::net::TcpStream;
use self::tokio_core::reactor::Core; use self::tokio_core::reactor::Core;
use tokio_io::codec::length_delimited::Framed; use tokio_io::codec::length_delimited::Framed;
#[test] #[test]
fn raw_encode_then_decode() { fn raw_encode_then_decode() {
let (data_tx, data_rx) = channel::<BytesMut>(256); let (data_tx, data_rx) = channel::<BytesMut>(256);
let data_tx = data_tx.sink_map_err::<_, IoError>(|_| panic!()); let data_tx = data_tx.sink_map_err::<_, IoError>(|_| panic!());
let data_rx = data_rx.map_err::<IoError, _>(|_| panic!()); let data_rx = data_rx.map_err::<IoError, _>(|_| panic!());
let cipher_key: [u8; 32] = rand::random(); let cipher_key: [u8; 32] = rand::random();
let hmac_key: [u8; 32] = rand::random(); let hmac_key: [u8; 32] = rand::random();
let encoder = let encoder = EncoderMiddleware::new(
EncoderMiddleware::new( data_tx,
data_tx, Box::new(CtrMode::new(
Box::new(CtrMode::new(AesSafe256Encryptor::new(&cipher_key), vec![0; 16])), AesSafe256Encryptor::new(&cipher_key),
SigningKey::new(&SHA256, &hmac_key), vec![0; 16],
); )),
let decoder = SigningKey::new(&SHA256, &hmac_key),
DecoderMiddleware::new( );
data_rx, let decoder = DecoderMiddleware::new(
Box::new(CtrMode::new(AesSafe256Encryptor::new(&cipher_key), vec![0; 16])), data_rx,
VerificationKey::new(&SHA256, &hmac_key), Box::new(CtrMode::new(
); AesSafe256Encryptor::new(&cipher_key),
vec![0; 16],
)),
VerificationKey::new(&SHA256, &hmac_key),
);
let data = b"hello world"; let data = b"hello world";
let data_sent = encoder.send(BytesMut::from(data.to_vec())).from_err(); let data_sent = encoder.send(BytesMut::from(data.to_vec())).from_err();
let data_received = decoder.into_future().map(|(n, _)| n).map_err(|(e, _)| e); let data_received = decoder.into_future().map(|(n, _)| n).map_err(|(e, _)| e);
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let (_, decoded) = core.run(data_sent.join(data_received)).map_err(|_| ()).unwrap(); let (_, decoded) = core.run(data_sent.join(data_received))
assert_eq!(decoded.unwrap(), data); .map_err(|_| ())
} .unwrap();
assert_eq!(decoded.unwrap(), data);
}
#[test] #[test]
fn full_codec_encode_then_decode() { fn full_codec_encode_then_decode() {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let cipher_key: [u8; 32] = rand::random(); let cipher_key: [u8; 32] = rand::random();
let cipher_key_clone = cipher_key.clone(); let cipher_key_clone = cipher_key.clone();
let hmac_key: [u8; 32] = rand::random(); let hmac_key: [u8; 32] = rand::random();
let hmac_key_clone = hmac_key.clone(); let hmac_key_clone = hmac_key.clone();
let data = b"hello world"; let data = b"hello world";
let data_clone = data.clone(); let data_clone = data.clone();
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap(); let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
let listener_addr = listener.local_addr().unwrap(); let listener_addr = listener.local_addr().unwrap();
let server = let server = listener.incoming().into_future().map_err(|(e, _)| e).map(
listener.incoming().into_future().map_err(|(e, _)| e).map(move |(connec, _)| { move |(connec, _)| {
let connec = Framed::new(connec.unwrap().0); let connec = Framed::new(connec.unwrap().0);
full_codec( full_codec(
connec, connec,
Box::new(CtrMode::new(AesSafe256Encryptor::new(&cipher_key), vec![0; 16])), Box::new(CtrMode::new(
SigningKey::new(&SHA256, &hmac_key), AesSafe256Encryptor::new(&cipher_key),
Box::new(CtrMode::new(AesSafe256Encryptor::new(&cipher_key), vec![0; 16])), vec![0; 16],
VerificationKey::new(&SHA256, &hmac_key), )),
) SigningKey::new(&SHA256, &hmac_key),
}); Box::new(CtrMode::new(
AesSafe256Encryptor::new(&cipher_key),
vec![0; 16],
)),
VerificationKey::new(&SHA256, &hmac_key),
)
},
);
let client = TcpStream::connect(&listener_addr, &core.handle()) let client = TcpStream::connect(&listener_addr, &core.handle())
.map_err(|e| e.into()) .map_err(|e| e.into())
.map(move |stream| { .map(move |stream| {
let stream = Framed::new(stream); let stream = Framed::new(stream);
full_codec( full_codec(
stream, stream,
Box::new(CtrMode::new(AesSafe256Encryptor::new(&cipher_key_clone), vec![0; 16])), Box::new(CtrMode::new(
SigningKey::new(&SHA256, &hmac_key_clone), AesSafe256Encryptor::new(&cipher_key_clone),
Box::new(CtrMode::new(AesSafe256Encryptor::new(&cipher_key_clone), vec![0; 16])), vec![0; 16],
VerificationKey::new(&SHA256, &hmac_key_clone), )),
) SigningKey::new(&SHA256, &hmac_key_clone),
}); Box::new(CtrMode::new(
AesSafe256Encryptor::new(&cipher_key_clone),
vec![0; 16],
)),
VerificationKey::new(&SHA256, &hmac_key_clone),
)
});
let fin = server.join(client) let fin = server
.from_err::<SecioError>() .join(client)
.and_then(|(server, client)| { .from_err::<SecioError>()
client.send(BytesMut::from(&data_clone[..])).map(move |_| server).from_err() .and_then(|(server, client)| {
}) client
.and_then(|server| server.into_future().map_err(|(e, _)| e.into())) .send(BytesMut::from(&data_clone[..]))
.map(|recved| recved.0.unwrap().to_vec()); .map(move |_| server)
.from_err()
})
.and_then(|server| server.into_future().map_err(|(e, _)| e.into()))
.map(|recved| recved.0.unwrap().to_vec());
let received = core.run(fin).unwrap(); let received = core.run(fin).unwrap();
assert_eq!(received, data); assert_eq!(received, data);
} }
} }

View File

@ -28,117 +28,99 @@ use std::io::Error as IoError;
/// Error at the SECIO layer communication. /// Error at the SECIO layer communication.
#[derive(Debug)] #[derive(Debug)]
pub enum SecioError { pub enum SecioError {
/// I/O error. /// I/O error.
IoError(IoError), IoError(IoError),
/// Failed to parse one of the handshake protobuf messages. /// Failed to parse one of the handshake protobuf messages.
HandshakeParsingFailure, HandshakeParsingFailure,
/// There is no protocol supported by both the local and remote hosts. /// There is no protocol supported by both the local and remote hosts.
NoSupportIntersection(&'static str, String), NoSupportIntersection(&'static str, String),
/// Failed to generate nonce. /// Failed to generate nonce.
NonceGenerationFailed, NonceGenerationFailed,
/// Failed to generate ephemeral key. /// Failed to generate ephemeral key.
EphemeralKeyGenerationFailed, EphemeralKeyGenerationFailed,
/// Failed to sign a message with our local private key. /// Failed to sign a message with our local private key.
SigningFailure, SigningFailure,
/// The signature of the exchange packet doesn't verify the remote public key. /// The signature of the exchange packet doesn't verify the remote public key.
SignatureVerificationFailed, SignatureVerificationFailed,
/// Failed to generate the secret shared key from the ephemeral key. /// Failed to generate the secret shared key from the ephemeral key.
SecretGenerationFailed, SecretGenerationFailed,
/// The final check of the handshake failed. /// The final check of the handshake failed.
NonceVerificationFailed, NonceVerificationFailed,
/// Error while decoding/encoding data. /// Error while decoding/encoding data.
CipherError(SymmetricCipherError), CipherError(SymmetricCipherError),
/// The received frame was of invalid length. /// The received frame was of invalid length.
FrameTooShort, FrameTooShort,
/// The hashes of the message didn't match. /// The hashes of the message didn't match.
HmacNotMatching, HmacNotMatching,
} }
impl error::Error for SecioError { impl error::Error for SecioError {
#[inline] #[inline]
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
SecioError::IoError(_) => { SecioError::IoError(_) => "I/O error",
"I/O error" SecioError::HandshakeParsingFailure => {
} "Failed to parse one of the handshake protobuf messages"
SecioError::HandshakeParsingFailure => { }
"Failed to parse one of the handshake protobuf messages" SecioError::NoSupportIntersection(_, _) => {
} "There is no protocol supported by both the local and remote hosts"
SecioError::NoSupportIntersection(_, _) => { }
"There is no protocol supported by both the local and remote hosts" SecioError::NonceGenerationFailed => "Failed to generate nonce",
} SecioError::EphemeralKeyGenerationFailed => "Failed to generate ephemeral key",
SecioError::NonceGenerationFailed => { SecioError::SigningFailure => "Failed to sign a message with our local private key",
"Failed to generate nonce" SecioError::SignatureVerificationFailed => {
} "The signature of the exchange packet doesn't verify the remote public key"
SecioError::EphemeralKeyGenerationFailed => { }
"Failed to generate ephemeral key" SecioError::SecretGenerationFailed => {
} "Failed to generate the secret shared key from the ephemeral key"
SecioError::SigningFailure => { }
"Failed to sign a message with our local private key" SecioError::NonceVerificationFailed => "The final check of the handshake failed",
} SecioError::CipherError(_) => "Error while decoding/encoding data",
SecioError::SignatureVerificationFailed => { SecioError::FrameTooShort => "The received frame was of invalid length",
"The signature of the exchange packet doesn't verify the remote public key" SecioError::HmacNotMatching => "The hashes of the message didn't match",
} }
SecioError::SecretGenerationFailed => { }
"Failed to generate the secret shared key from the ephemeral key"
}
SecioError::NonceVerificationFailed => {
"The final check of the handshake failed"
}
SecioError::CipherError(_) => {
"Error while decoding/encoding data"
}
SecioError::FrameTooShort => {
"The received frame was of invalid length"
}
SecioError::HmacNotMatching => {
"The hashes of the message didn't match"
}
}
}
fn cause(&self) -> Option<&error::Error> { fn cause(&self) -> Option<&error::Error> {
match *self { match *self {
SecioError::IoError(ref err) => { SecioError::IoError(ref err) => Some(err),
Some(err) // TODO: The type doesn't implement `Error`
} /*SecioError::CipherError(ref err) => {
// TODO: The type doesn't implement `Error` Some(err)
/*SecioError::CipherError(ref err) => { },*/
Some(err) _ => None,
},*/ }
_ => None, }
}
}
} }
impl fmt::Display for SecioError { impl fmt::Display for SecioError {
#[inline] #[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self)) write!(fmt, "{}", error::Error::description(self))
} }
} }
impl From<SymmetricCipherError> for SecioError { impl From<SymmetricCipherError> for SecioError {
#[inline] #[inline]
fn from(err: SymmetricCipherError) -> SecioError { fn from(err: SymmetricCipherError) -> SecioError {
SecioError::CipherError(err) SecioError::CipherError(err)
} }
} }
impl From<IoError> for SecioError { impl From<IoError> for SecioError {
#[inline] #[inline]
fn from(err: IoError) -> SecioError { fn from(err: IoError) -> SecioError {
SecioError::IoError(err) SecioError::IoError(err)
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -22,12 +22,12 @@
//! through a socket (or anything that implements `AsyncRead + AsyncWrite`). //! through a socket (or anything that implements `AsyncRead + AsyncWrite`).
//! //!
//! # Connection upgrade //! # Connection upgrade
//! //!
//! The `SecioConfig` struct implements the `ConnectionUpgrade` trait. You can apply it over a //! The `SecioConfig` struct implements the `ConnectionUpgrade` trait. You can apply it over a
//! `Transport` by using the `with_upgrade` method. The returned object will also implement //! `Transport` by using the `with_upgrade` method. The returned object will also implement
//! `Transport` and will automatically apply the secio protocol over any connection that is opened //! `Transport` and will automatically apply the secio protocol over any connection that is opened
//! through it. //! through it.
//! //!
//! ```no_run //! ```no_run
//! extern crate futures; //! extern crate futures;
//! extern crate tokio_core; //! extern crate tokio_core;
@ -35,7 +35,7 @@
//! extern crate libp2p_swarm; //! extern crate libp2p_swarm;
//! extern crate libp2p_secio; //! extern crate libp2p_secio;
//! extern crate libp2p_tcp_transport; //! extern crate libp2p_tcp_transport;
//! //!
//! # fn main() { //! # fn main() {
//! use futures::Future; //! use futures::Future;
//! use libp2p_secio::{SecioConfig, SecioKeyPair}; //! use libp2p_secio::{SecioConfig, SecioKeyPair};
@ -43,28 +43,28 @@
//! use libp2p_tcp_transport::TcpConfig; //! use libp2p_tcp_transport::TcpConfig;
//! use tokio_core::reactor::Core; //! use tokio_core::reactor::Core;
//! use tokio_io::io::write_all; //! use tokio_io::io::write_all;
//! //!
//! let mut core = Core::new().unwrap(); //! let mut core = Core::new().unwrap();
//! //!
//! let transport = TcpConfig::new(core.handle()) //! let transport = TcpConfig::new(core.handle())
//! .with_upgrade({ //! .with_upgrade({
//! # let private_key = b""; //! # let private_key = b"";
//! //let private_key = include_bytes!("test-private-key.pk8"); //! //let private_key = include_bytes!("test-private-key.pk8");
//! # let public_key = vec![]; //! # let public_key = vec![];
//! //let public_key = include_bytes!("test-public-key.der").to_vec(); //! //let public_key = include_bytes!("test-public-key.der").to_vec();
//! SecioConfig { //! SecioConfig {
//! // See the documentation of `SecioKeyPair`. //! // See the documentation of `SecioKeyPair`.
//! key: SecioKeyPair::rsa_from_pkcs8(private_key, public_key).unwrap(), //! key: SecioKeyPair::rsa_from_pkcs8(private_key, public_key).unwrap(),
//! } //! }
//! }); //! });
//! //!
//! let future = transport.dial("/ip4/127.0.0.1/tcp/12345".parse::<Multiaddr>().unwrap()) //! let future = transport.dial("/ip4/127.0.0.1/tcp/12345".parse::<Multiaddr>().unwrap())
//! .unwrap_or_else(|_| panic!("Unable to dial node")) //! .unwrap_or_else(|_| panic!("Unable to dial node"))
//! .and_then(|(connection, _)| { //! .and_then(|(connection, _)| {
//! // Sends "hello world" on the connection, will be encrypted. //! // Sends "hello world" on the connection, will be encrypted.
//! write_all(connection, "hello world") //! write_all(connection, "hello world")
//! }); //! });
//! //!
//! core.run(future).unwrap(); //! core.run(future).unwrap();
//! # } //! # }
//! ``` //! ```
@ -95,7 +95,7 @@ extern crate untrusted;
pub use self::error::SecioError; pub use self::error::SecioError;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::{Future, Poll, StartSend, Sink, Stream}; use futures::{Future, Poll, Sink, StartSend, Stream};
use futures::stream::MapErr as StreamMapErr; use futures::stream::MapErr as StreamMapErr;
use libp2p_swarm::Multiaddr; use libp2p_swarm::Multiaddr;
use ring::signature::RSAKeyPair; use ring::signature::RSAKeyPair;
@ -118,8 +118,8 @@ mod structs_proto;
/// secio on any connection. /// secio on any connection.
#[derive(Clone)] #[derive(Clone)]
pub struct SecioConfig { pub struct SecioConfig {
/// Private and public keys of the local node. /// Private and public keys of the local node.
pub key: SecioKeyPair, pub key: SecioKeyPair,
} }
/// Private and public keys of the local node. /// Private and public keys of the local node.
@ -141,86 +141,86 @@ pub struct SecioConfig {
/// ///
/// ```ignore /// ```ignore
/// let key_pair = SecioKeyPair::rsa_from_pkcs8(include_bytes!("private.pk8"), /// let key_pair = SecioKeyPair::rsa_from_pkcs8(include_bytes!("private.pk8"),
/// include_bytes!("public.der")); /// include_bytes!("public.der"));
/// ``` /// ```
/// ///
#[derive(Clone)] #[derive(Clone)]
pub struct SecioKeyPair { pub struct SecioKeyPair {
inner: SecioKeyPairInner, inner: SecioKeyPairInner,
} }
impl SecioKeyPair { impl SecioKeyPair {
pub fn rsa_from_pkcs8<P>(private: &[u8], public: P) pub fn rsa_from_pkcs8<P>(
-> Result<SecioKeyPair, Box<Error + Send + Sync>> private: &[u8],
where P: Into<Vec<u8>> public: P,
{ ) -> Result<SecioKeyPair, Box<Error + Send + Sync>>
let private = RSAKeyPair::from_pkcs8(Input::from(&private[..])) where
.map_err(|err| Box::new(err))?; P: Into<Vec<u8>>,
{
let private =
RSAKeyPair::from_pkcs8(Input::from(&private[..])).map_err(|err| Box::new(err))?;
Ok(SecioKeyPair { Ok(SecioKeyPair {
inner: SecioKeyPairInner::Rsa { inner: SecioKeyPairInner::Rsa {
public: public.into(), public: public.into(),
private: Arc::new(private), private: Arc::new(private),
} },
}) })
} }
} }
// Inner content of `SecioKeyPair`. // Inner content of `SecioKeyPair`.
#[derive(Clone)] #[derive(Clone)]
enum SecioKeyPairInner { enum SecioKeyPairInner {
Rsa { Rsa {
public: Vec<u8>, public: Vec<u8>,
private: Arc<RSAKeyPair>, private: Arc<RSAKeyPair>,
} },
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum SecioPublicKey<'a> { pub enum SecioPublicKey<'a> {
/// DER format. /// DER format.
Rsa(&'a [u8]), Rsa(&'a [u8]),
} }
impl<S> libp2p_swarm::ConnectionUpgrade<S> for SecioConfig impl<S> libp2p_swarm::ConnectionUpgrade<S> for SecioConfig
where S: AsyncRead + AsyncWrite + 'static where
S: AsyncRead + AsyncWrite + 'static,
{ {
type Output = RwStreamSink< type Output = RwStreamSink<StreamMapErr<SecioMiddleware<S>, fn(SecioError) -> IoError>>;
StreamMapErr< type Future = Box<Future<Item = Self::Output, Error = IoError>>;
SecioMiddleware<S>, type NamesIter = iter::Once<(Bytes, ())>;
fn(SecioError) -> IoError, type UpgradeIdentifier = ();
>,
>;
type Future = Box<Future<Item = Self::Output, Error = IoError>>;
type NamesIter = iter::Once<(Bytes, ())>;
type UpgradeIdentifier = ();
#[inline] #[inline]
fn protocol_names(&self) -> Self::NamesIter { fn protocol_names(&self) -> Self::NamesIter {
iter::once(("/secio/1.0.0".into(), ())) iter::once(("/secio/1.0.0".into(), ()))
} }
#[inline] #[inline]
fn upgrade(self, incoming: S, _: (), _: libp2p_swarm::Endpoint, remote_addr: &Multiaddr) fn upgrade(
-> Self::Future self,
{ incoming: S,
info!(target: "libp2p-secio", "starting secio upgrade with {:?}", remote_addr); _: (),
_: libp2p_swarm::Endpoint,
remote_addr: &Multiaddr,
) -> Self::Future {
info!(target: "libp2p-secio", "starting secio upgrade with {:?}", remote_addr);
let fut = SecioMiddleware::handshake( let fut = SecioMiddleware::handshake(incoming, self.key);
incoming, let wrapped = fut.map(|stream_sink| {
self.key, let mapped = stream_sink.map_err(map_err as fn(_) -> _);
); RwStreamSink::new(mapped)
let wrapped = fut.map(|stream_sink| { }).map_err(map_err);
let mapped = stream_sink.map_err(map_err as fn(_) -> _); Box::new(wrapped)
RwStreamSink::new(mapped) }
}).map_err(map_err);
Box::new(wrapped)
}
} }
#[inline] #[inline]
fn map_err(err: SecioError) -> IoError { fn map_err(err: SecioError) -> IoError {
debug!(target: "libp2p-secio", "error during secio handshake {:?}", err); debug!(target: "libp2p-secio", "error during secio handshake {:?}", err);
IoError::new(IoErrorKind::InvalidData, err) IoError::new(IoErrorKind::InvalidData, err)
} }
/// Wraps around an object that implements `AsyncRead` and `AsyncWrite`. /// Wraps around an object that implements `AsyncRead` and `AsyncWrite`.
@ -228,67 +228,69 @@ fn map_err(err: SecioError) -> IoError {
/// Implements `Sink` and `Stream` whose items are frames of data. Each frame is encoded /// Implements `Sink` and `Stream` whose items are frames of data. Each frame is encoded
/// individually, so you are encouraged to group data in few frames if possible. /// individually, so you are encouraged to group data in few frames if possible.
pub struct SecioMiddleware<S> { pub struct SecioMiddleware<S> {
inner: codec::FullCodec<S>, inner: codec::FullCodec<S>,
remote_pubkey_der: Vec<u8>, remote_pubkey_der: Vec<u8>,
} }
impl<S> SecioMiddleware<S> impl<S> SecioMiddleware<S>
where S: AsyncRead + AsyncWrite where
S: AsyncRead + AsyncWrite,
{ {
/// Attempts to perform a handshake on the given socket. /// Attempts to perform a handshake on the given socket.
/// ///
/// On success, produces a `SecioMiddleware` that can then be used to encode/decode /// On success, produces a `SecioMiddleware` that can then be used to encode/decode
/// communications. /// communications.
pub fn handshake<'a>( pub fn handshake<'a>(
socket: S, socket: S,
key_pair: SecioKeyPair, key_pair: SecioKeyPair,
) -> Box<Future<Item = SecioMiddleware<S>, Error = SecioError> + 'a> ) -> Box<Future<Item = SecioMiddleware<S>, Error = SecioError> + 'a>
where S: 'a where
{ S: 'a,
let SecioKeyPairInner::Rsa { private, public } = key_pair.inner; {
let SecioKeyPairInner::Rsa { private, public } = key_pair.inner;
let fut = handshake::handshake(socket, public, private) let fut =
.map(|(inner, pubkey)| { handshake::handshake(socket, public, private).map(|(inner, pubkey)| SecioMiddleware {
SecioMiddleware { inner: inner,
inner: inner, remote_pubkey_der: pubkey,
remote_pubkey_der: pubkey, });
} Box::new(fut)
}); }
Box::new(fut)
}
/// Returns the public key of the remote in the `DER` format. /// Returns the public key of the remote in the `DER` format.
#[inline] #[inline]
pub fn remote_public_key_der(&self) -> SecioPublicKey { pub fn remote_public_key_der(&self) -> SecioPublicKey {
SecioPublicKey::Rsa(&self.remote_pubkey_der) SecioPublicKey::Rsa(&self.remote_pubkey_der)
} }
} }
impl<S> Sink for SecioMiddleware<S> impl<S> Sink for SecioMiddleware<S>
where S: AsyncRead + AsyncWrite where
S: AsyncRead + AsyncWrite,
{ {
type SinkItem = BytesMut; type SinkItem = BytesMut;
type SinkError = IoError; type SinkError = IoError;
#[inline] #[inline]
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> { fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
self.inner.start_send(item) self.inner.start_send(item)
} }
#[inline] #[inline]
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
self.inner.poll_complete() self.inner.poll_complete()
} }
} }
impl<S> Stream for SecioMiddleware<S> impl<S> Stream for SecioMiddleware<S>
where S: AsyncRead + AsyncWrite where
S: AsyncRead + AsyncWrite,
{ {
type Item = Vec<u8>; type Item = Vec<u8>;
type Error = SecioError; type Error = SecioError;
#[inline] #[inline]
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
self.inner.poll() self.inner.poll()
} }
} }

View File

@ -40,7 +40,7 @@
//! `MuxedTransport` trait. //! `MuxedTransport` trait.
use fnv::FnvHashMap; use fnv::FnvHashMap;
use futures::future::{self, IntoFuture, FutureResult}; use futures::future::{self, FutureResult, IntoFuture};
use futures::{Async, Future, Poll, Stream}; use futures::{Async, Future, Poll, Stream};
use futures::stream::Fuse as StreamFuse; use futures::stream::Fuse as StreamFuse;
use futures::sync::mpsc; use futures::sync::mpsc;
@ -59,289 +59,323 @@ use transport::{ConnectionUpgrade, MuxedTransport, Transport, UpgradedNode};
#[derive(Clone)] #[derive(Clone)]
pub struct ConnectionReuse<T, C> pub struct ConnectionReuse<T, C>
where where
T: Transport, T: Transport,
C: ConnectionUpgrade<T::RawConn>, C: ConnectionUpgrade<T::RawConn>,
C::Output: StreamMuxer, C::Output: StreamMuxer,
{ {
// Underlying transport and connection upgrade for when we need to dial or listen. // Underlying transport and connection upgrade for when we need to dial or listen.
inner: UpgradedNode<T, C>, inner: UpgradedNode<T, C>,
// Struct shared between most of the `ConnectionReuse` infrastructure. // Struct shared between most of the `ConnectionReuse` infrastructure.
shared: Arc<Mutex<Shared<C::Output>>>, shared: Arc<Mutex<Shared<C::Output>>>,
} }
struct Shared<M> where M: StreamMuxer { struct Shared<M>
// List of active muxers. where
active_connections: FnvHashMap<Multiaddr, M>, M: StreamMuxer,
{
// List of active muxers.
active_connections: FnvHashMap<Multiaddr, M>,
// List of pending inbound substreams from dialed nodes. // List of pending inbound substreams from dialed nodes.
// Only add to this list elements received through `add_to_next_rx`. // Only add to this list elements received through `add_to_next_rx`.
next_incoming: Vec<(M, M::InboundSubstream, Multiaddr)>, next_incoming: Vec<(M, M::InboundSubstream, Multiaddr)>,
// New elements are not directly added to `next_incoming`. Instead they are sent to this // New elements are not directly added to `next_incoming`. Instead they are sent to this
// channel. This is done so that we can wake up tasks whenever a new element is added. // channel. This is done so that we can wake up tasks whenever a new element is added.
add_to_next_rx: mpsc::UnboundedReceiver<(M, M::InboundSubstream, Multiaddr)>, add_to_next_rx: mpsc::UnboundedReceiver<(M, M::InboundSubstream, Multiaddr)>,
// Other side of `add_to_next_rx`. // Other side of `add_to_next_rx`.
add_to_next_tx: mpsc::UnboundedSender<(M, M::InboundSubstream, Multiaddr)>, add_to_next_tx: mpsc::UnboundedSender<(M, M::InboundSubstream, Multiaddr)>,
} }
impl<T, C> From<UpgradedNode<T, C>> for ConnectionReuse<T, C> impl<T, C> From<UpgradedNode<T, C>> for ConnectionReuse<T, C>
where where
T: Transport, T: Transport,
C: ConnectionUpgrade<T::RawConn>, C: ConnectionUpgrade<T::RawConn>,
C::Output: StreamMuxer, C::Output: StreamMuxer,
{ {
#[inline] #[inline]
fn from(node: UpgradedNode<T, C>) -> ConnectionReuse<T, C> { fn from(node: UpgradedNode<T, C>) -> ConnectionReuse<T, C> {
let (tx, rx) = mpsc::unbounded(); let (tx, rx) = mpsc::unbounded();
ConnectionReuse { ConnectionReuse {
inner: node, inner: node,
shared: Arc::new(Mutex::new(Shared { shared: Arc::new(Mutex::new(Shared {
active_connections: Default::default(), active_connections: Default::default(),
next_incoming: Vec::new(), next_incoming: Vec::new(),
add_to_next_rx: rx, add_to_next_rx: rx,
add_to_next_tx: tx, add_to_next_tx: tx,
})), })),
} }
} }
} }
impl<T, C> Transport for ConnectionReuse<T, C> impl<T, C> Transport for ConnectionReuse<T, C>
where where
T: Transport + 'static, // TODO: 'static :( T: Transport + 'static, // TODO: 'static :(
C: ConnectionUpgrade<T::RawConn> + 'static, // TODO: 'static :( C: ConnectionUpgrade<T::RawConn> + 'static, // TODO: 'static :(
C: Clone, C: Clone,
C::Output: StreamMuxer + Clone, C::Output: StreamMuxer + Clone,
C::NamesIter: Clone, // TODO: not elegant C::NamesIter: Clone, // TODO: not elegant
{ {
type RawConn = <C::Output as StreamMuxer>::Substream; type RawConn = <C::Output as StreamMuxer>::Substream;
type Listener = Box<Stream<Item = Self::ListenerUpgrade, Error = IoError>>; type Listener = Box<Stream<Item = Self::ListenerUpgrade, Error = IoError>>;
type ListenerUpgrade = FutureResult<(Self::RawConn, Multiaddr), IoError>; type ListenerUpgrade = FutureResult<(Self::RawConn, Multiaddr), IoError>;
type Dial = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>; type Dial = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>;
fn listen_on(self, addr: Multiaddr) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> { fn listen_on(self, addr: Multiaddr) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> {
let (listener, new_addr) = match self.inner.listen_on(addr.clone()) { let (listener, new_addr) = match self.inner.listen_on(addr.clone()) {
Ok((l, a)) => (l, a), Ok((l, a)) => (l, a),
Err((inner, addr)) => { Err((inner, addr)) => {
return Err((ConnectionReuse { inner: inner, shared: self.shared }, addr)); return Err((
} ConnectionReuse {
}; inner: inner,
shared: self.shared,
},
addr,
));
}
};
let listener = ConnectionReuseListener { let listener = ConnectionReuseListener {
shared: self.shared.clone(), shared: self.shared.clone(),
listener: listener.fuse(), listener: listener.fuse(),
current_upgrades: Vec::new(), current_upgrades: Vec::new(),
connections: Vec::new(), connections: Vec::new(),
}; };
Ok((Box::new(listener) as Box<_>, new_addr)) Ok((Box::new(listener) as Box<_>, new_addr))
} }
fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> { fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> {
// If we already have an active connection, use it! // If we already have an active connection, use it!
if let Some(connec) = self.shared.lock().active_connections.get(&addr).map(|c| c.clone()) { if let Some(connec) = self.shared
let future = connec.outbound().map(|s| (s, addr)); .lock()
return Ok(Box::new(future) as Box<_>); .active_connections
} .get(&addr)
.map(|c| c.clone())
{
let future = connec.outbound().map(|s| (s, addr));
return Ok(Box::new(future) as Box<_>);
}
// TODO: handle if we're already in the middle in dialing that same node? // TODO: handle if we're already in the middle in dialing that same node?
// TODO: try dialing again if the existing connection has dropped // TODO: try dialing again if the existing connection has dropped
let dial = match self.inner.dial(addr) { let dial = match self.inner.dial(addr) {
Ok(l) => l, Ok(l) => l,
Err((inner, addr)) => { Err((inner, addr)) => {
return Err((ConnectionReuse { inner: inner, shared: self.shared }, addr)); return Err((
} ConnectionReuse {
}; inner: inner,
shared: self.shared,
},
addr,
));
}
};
let shared = self.shared.clone(); let shared = self.shared.clone();
let dial = dial let dial = dial.into_future().and_then(move |(connec, addr)| {
.into_future() // Always replace the active connection because we are the most recent.
.and_then(move |(connec, addr)| { let mut lock = shared.lock();
// Always replace the active connection because we are the most recent. lock.active_connections.insert(addr.clone(), connec.clone());
let mut lock = shared.lock(); // TODO: doesn't need locking ; the sender could be extracted
lock.active_connections.insert(addr.clone(), connec.clone()); let _ = lock.add_to_next_tx.unbounded_send((
// TODO: doesn't need locking ; the sender could be extracted connec.clone(),
let _ = lock.add_to_next_tx connec.clone().inbound(),
.unbounded_send((connec.clone(), connec.clone().inbound(), addr.clone())); addr.clone(),
connec.outbound().map(|s| (s, addr)) ));
}); connec.outbound().map(|s| (s, addr))
});
Ok(Box::new(dial) as Box<_>) Ok(Box::new(dial) as Box<_>)
} }
#[inline] #[inline]
fn nat_traversal(&self, server: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> { fn nat_traversal(&self, server: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> {
self.inner.transport().nat_traversal(server, observed) self.inner.transport().nat_traversal(server, observed)
} }
} }
impl<T, C> MuxedTransport for ConnectionReuse<T, C> impl<T, C> MuxedTransport for ConnectionReuse<T, C>
where where
T: Transport + 'static, // TODO: 'static :( T: Transport + 'static, // TODO: 'static :(
C: ConnectionUpgrade<T::RawConn> + 'static, // TODO: 'static :( C: ConnectionUpgrade<T::RawConn> + 'static, // TODO: 'static :(
C: Clone, C: Clone,
C::Output: StreamMuxer + Clone, C::Output: StreamMuxer + Clone,
C::NamesIter: Clone, // TODO: not elegant C::NamesIter: Clone, // TODO: not elegant
{ {
type Incoming = ConnectionReuseIncoming<C::Output>; type Incoming = ConnectionReuseIncoming<C::Output>;
type IncomingUpgrade = future::FutureResult<(<C::Output as StreamMuxer>::Substream, Multiaddr), IoError>; type IncomingUpgrade =
future::FutureResult<(<C::Output as StreamMuxer>::Substream, Multiaddr), IoError>;
#[inline] #[inline]
fn next_incoming(self) -> Self::Incoming { fn next_incoming(self) -> Self::Incoming {
ConnectionReuseIncoming { shared: self.shared.clone() } ConnectionReuseIncoming {
} shared: self.shared.clone(),
}
}
} }
/// Implementation of `Stream` for the connections incoming from listening on a specific address. /// Implementation of `Stream` for the connections incoming from listening on a specific address.
pub struct ConnectionReuseListener<S, F, M> pub struct ConnectionReuseListener<S, F, M>
where where
S: Stream<Item = F, Error = IoError>, S: Stream<Item = F, Error = IoError>,
F: Future<Item = (M, Multiaddr), Error = IoError>, F: Future<Item = (M, Multiaddr), Error = IoError>,
M: StreamMuxer, M: StreamMuxer,
{ {
// The main listener. `S` is from the underlying transport. // The main listener. `S` is from the underlying transport.
listener: StreamFuse<S>, listener: StreamFuse<S>,
current_upgrades: Vec<F>, current_upgrades: Vec<F>,
connections: Vec<(M, <M as StreamMuxer>::InboundSubstream, Multiaddr)>, connections: Vec<(M, <M as StreamMuxer>::InboundSubstream, Multiaddr)>,
// Shared between the whole connection reuse mechanism. // Shared between the whole connection reuse mechanism.
shared: Arc<Mutex<Shared<M>>>, shared: Arc<Mutex<Shared<M>>>,
} }
impl<S, F, M> Stream for ConnectionReuseListener<S, F, M> impl<S, F, M> Stream for ConnectionReuseListener<S, F, M>
where S: Stream<Item = F, Error = IoError>, where
F: Future<Item = (M, Multiaddr), Error = IoError>, S: Stream<Item = F, Error = IoError>,
M: StreamMuxer + Clone + 'static // TODO: 'static :( F: Future<Item = (M, Multiaddr), Error = IoError>,
M: StreamMuxer + Clone + 'static, // TODO: 'static :(
{ {
type Item = FutureResult<(M::Substream, Multiaddr), IoError>; type Item = FutureResult<(M::Substream, Multiaddr), IoError>;
type Error = IoError; type Error = IoError;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
// Check for any incoming connection on the listening socket. // Check for any incoming connection on the listening socket.
// Note that since `self.listener` is a `Fuse`, it's not a problem to continue polling even // Note that since `self.listener` is a `Fuse`, it's not a problem to continue polling even
// after it is finished or after it error'ed. // after it is finished or after it error'ed.
match self.listener.poll() { match self.listener.poll() {
Ok(Async::Ready(Some(upgrade))) => { Ok(Async::Ready(Some(upgrade))) => {
self.current_upgrades.push(upgrade); self.current_upgrades.push(upgrade);
} }
Ok(Async::NotReady) => {}, Ok(Async::NotReady) => {}
Ok(Async::Ready(None)) => { Ok(Async::Ready(None)) => {
if self.connections.is_empty() && self.current_upgrades.is_empty() { if self.connections.is_empty() && self.current_upgrades.is_empty() {
return Ok(Async::Ready(None)); return Ok(Async::Ready(None));
} }
} }
Err(err) => { Err(err) => {
if self.connections.is_empty() && self.current_upgrades.is_empty() { if self.connections.is_empty() && self.current_upgrades.is_empty() {
return Err(err); return Err(err);
} }
} }
}; };
// Check whether any upgrade (to a muxer) on an incoming connection is ready. // Check whether any upgrade (to a muxer) on an incoming connection is ready.
// We extract everything at the start, then insert back the elements that we still want at // We extract everything at the start, then insert back the elements that we still want at
// the next iteration. // the next iteration.
for n in (0 .. self.current_upgrades.len()).rev() { for n in (0..self.current_upgrades.len()).rev() {
let mut current_upgrade = self.current_upgrades.swap_remove(n); let mut current_upgrade = self.current_upgrades.swap_remove(n);
match current_upgrade.poll() { match current_upgrade.poll() {
Ok(Async::Ready((muxer, client_addr))) => { Ok(Async::Ready((muxer, client_addr))) => {
let next_incoming = muxer.clone().inbound(); let next_incoming = muxer.clone().inbound();
self.connections.push((muxer.clone(), next_incoming, client_addr.clone())); self.connections
// We overwrite any current active connection to that multiaddr because we .push((muxer.clone(), next_incoming, client_addr.clone()));
// are the freshest possible connection. // We overwrite any current active connection to that multiaddr because we
self.shared.lock().active_connections.insert(client_addr, muxer); // are the freshest possible connection.
}, self.shared
Ok(Async::NotReady) => { .lock()
self.current_upgrades.push(current_upgrade); .active_connections
}, .insert(client_addr, muxer);
Err(err) => { }
// Insert the rest of the pending upgrades, but not the current one. Ok(Async::NotReady) => {
return Ok(Async::Ready(Some(future::err(err)))); self.current_upgrades.push(current_upgrade);
}, }
} Err(err) => {
} // Insert the rest of the pending upgrades, but not the current one.
return Ok(Async::Ready(Some(future::err(err))));
}
}
}
// Check whether any incoming substream is ready. // Check whether any incoming substream is ready.
for n in (0 .. self.connections.len()).rev() { for n in (0..self.connections.len()).rev() {
let (muxer, mut next_incoming, client_addr) = self.connections.swap_remove(n); let (muxer, mut next_incoming, client_addr) = self.connections.swap_remove(n);
match next_incoming.poll() { match next_incoming.poll() {
Ok(Async::Ready(incoming)) => { Ok(Async::Ready(incoming)) => {
// A new substream is ready. // A new substream is ready.
let mut new_next = muxer.clone().inbound(); let mut new_next = muxer.clone().inbound();
self.connections.push((muxer, new_next, client_addr.clone())); self.connections
return Ok(Async::Ready(Some(Ok((incoming, client_addr)).into_future()))); .push((muxer, new_next, client_addr.clone()));
} return Ok(Async::Ready(Some(
Ok(Async::NotReady) => { Ok((incoming, client_addr)).into_future(),
self.connections.push((muxer, next_incoming, client_addr)); )));
} }
Err(err) => { Ok(Async::NotReady) => {
// Insert the rest of the pending connections, but not the current one. self.connections.push((muxer, next_incoming, client_addr));
return Ok(Async::Ready(Some(future::err(err)))); }
} Err(err) => {
} // Insert the rest of the pending connections, but not the current one.
} return Ok(Async::Ready(Some(future::err(err))));
}
}
}
// Nothing is ready, return `NotReady`. // Nothing is ready, return `NotReady`.
Ok(Async::NotReady) Ok(Async::NotReady)
} }
} }
/// Implementation of `Future` that yields the next incoming substream from a dialed connection. /// Implementation of `Future` that yields the next incoming substream from a dialed connection.
pub struct ConnectionReuseIncoming<M> pub struct ConnectionReuseIncoming<M>
where M: StreamMuxer where
M: StreamMuxer,
{ {
// Shared between the whole connection reuse system. // Shared between the whole connection reuse system.
shared: Arc<Mutex<Shared<M>>>, shared: Arc<Mutex<Shared<M>>>,
} }
impl<M> Future for ConnectionReuseIncoming<M> impl<M> Future for ConnectionReuseIncoming<M>
where M: Clone + StreamMuxer, where
M: Clone + StreamMuxer,
{ {
type Item = future::FutureResult<(M::Substream, Multiaddr), IoError>; type Item = future::FutureResult<(M::Substream, Multiaddr), IoError>;
type Error = IoError; type Error = IoError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let mut lock = self.shared.lock(); let mut lock = self.shared.lock();
// Try to get any new muxer from `add_to_next_rx`. // Try to get any new muxer from `add_to_next_rx`.
// We push the new muxers to a channel instead of adding them to `next_incoming`, so that // We push the new muxers to a channel instead of adding them to `next_incoming`, so that
// tasks are notified when something is pushed. // tasks are notified when something is pushed.
loop { loop {
match lock.add_to_next_rx.poll() { match lock.add_to_next_rx.poll() {
Ok(Async::Ready(Some(elem))) => { Ok(Async::Ready(Some(elem))) => {
lock.next_incoming.push(elem); lock.next_incoming.push(elem);
}, }
Ok(Async::NotReady) => break, Ok(Async::NotReady) => break,
Ok(Async::Ready(None)) | Err(_) => { Ok(Async::Ready(None)) | Err(_) => unreachable!(
unreachable!("the sender and receiver are both in the same struct, therefore \ "the sender and receiver are both in the same struct, therefore \
the link can never break") the link can never break"
}, ),
} }
} }
// Check whether any incoming substream is ready. // Check whether any incoming substream is ready.
for n in (0 .. lock.next_incoming.len()).rev() { for n in (0..lock.next_incoming.len()).rev() {
let (muxer, mut future, addr) = lock.next_incoming.swap_remove(n); let (muxer, mut future, addr) = lock.next_incoming.swap_remove(n);
match future.poll() { match future.poll() {
Ok(Async::Ready(value)) => { Ok(Async::Ready(value)) => {
// A substream is ready ; push back the muxer for the next time this function // A substream is ready ; push back the muxer for the next time this function
// is called, then return. // is called, then return.
let next = muxer.clone().inbound(); let next = muxer.clone().inbound();
lock.next_incoming.push((muxer, next, addr.clone())); lock.next_incoming.push((muxer, next, addr.clone()));
return Ok(Async::Ready(future::ok((value, addr)))); return Ok(Async::Ready(future::ok((value, addr))));
}, }
Ok(Async::NotReady) => { Ok(Async::NotReady) => {
lock.next_incoming.push((muxer, future, addr)); lock.next_incoming.push((muxer, future, addr));
}, }
Err(_) => { Err(_) => {
// In case of error, we just not push back the element, which drops it. // In case of error, we just not push back the element, which drops it.
}, }
} }
} }
// Nothing is ready. // Nothing is ready.
Ok(Async::NotReady) Ok(Async::NotReady)
} }
} }

View File

@ -22,60 +22,60 @@
//#![doc(include = "../README.md")] //#![doc(include = "../README.md")]
//! Transport, protocol upgrade and swarm systems of *libp2p*. //! Transport, protocol upgrade and swarm systems of *libp2p*.
//! //!
//! This crate contains all the core traits and mechanisms of the transport and swarm systems //! This crate contains all the core traits and mechanisms of the transport and swarm systems
//! of *libp2p*. //! of *libp2p*.
//! //!
//! # The `Transport` trait //! # The `Transport` trait
//! //!
//! The main trait that this crate provides is `Transport`, which provides the `dial` and //! The main trait that this crate provides is `Transport`, which provides the `dial` and
//! `listen_on` methods and can be used to dial or listen on a multiaddress. The `swarm` crate //! `listen_on` methods and can be used to dial or listen on a multiaddress. The `swarm` crate
//! itself does not provide any concrete (ie. non-dummy, non-adapter) implementation of this trait. //! itself does not provide any concrete (ie. non-dummy, non-adapter) implementation of this trait.
//! It is implemented on structs that are provided by external crates, such as `TcpConfig` from //! It is implemented on structs that are provided by external crates, such as `TcpConfig` from
//! `tcp-transport`, `UdpConfig`, or `WebsocketConfig` (note: as of the writing of this //! `tcp-transport`, `UdpConfig`, or `WebsocketConfig` (note: as of the writing of this
//! documentation, the last two structs don't exist yet). //! documentation, the last two structs don't exist yet).
//! //!
//! Each implementation of `Transport` only supports *some* multiaddress protocols, for example //! Each implementation of `Transport` only supports *some* multiaddress protocols, for example
//! the `TcpConfig` struct only supports multiaddresses that look like `/ip*/*.*.*.*/tcp/*`. It is //! the `TcpConfig` struct only supports multiaddresses that look like `/ip*/*.*.*.*/tcp/*`. It is
//! possible to group two implementations of `Transport` with the `or_transport` method, in order //! possible to group two implementations of `Transport` with the `or_transport` method, in order
//! to obtain a single object that supports the protocols of both objects at once. This can be done //! to obtain a single object that supports the protocols of both objects at once. This can be done
//! multiple times in a row in order to chain as many implementations as you want. //! multiple times in a row in order to chain as many implementations as you want.
//! //!
//! // TODO: right now only tcp-transport exists, we need to add an example for chaining //! // TODO: right now only tcp-transport exists, we need to add an example for chaining
//! // multiple transports once that makes sense //! // multiple transports once that makes sense
//! //!
//! ## The `MuxedTransport` trait //! ## The `MuxedTransport` trait
//! //!
//! The `MuxedTransport` trait is an extension to the `Transport` trait, and is implemented on //! The `MuxedTransport` trait is an extension to the `Transport` trait, and is implemented on
//! transports that can receive incoming connections on streams that have been opened with `dial()`. //! transports that can receive incoming connections on streams that have been opened with `dial()`.
//! //!
//! The trait provides the `next_incoming()` method, which returns a future that will resolve to //! The trait provides the `next_incoming()` method, which returns a future that will resolve to
//! the next substream that arrives from a dialed node. //! the next substream that arrives from a dialed node.
//! //!
//! > **Note**: This trait is mainly implemented for transports that provide stream muxing //! > **Note**: This trait is mainly implemented for transports that provide stream muxing
//! > capabilities, but it can also be implemented in a dummy way by returning an empty //! > capabilities, but it can also be implemented in a dummy way by returning an empty
//! > iterator. //! > iterator.
//! //!
//! # Connection upgrades //! # Connection upgrades
//! //!
//! Once a socket has been opened with a remote through a `Transport`, it can be *upgraded*. This //! Once a socket has been opened with a remote through a `Transport`, it can be *upgraded*. This
//! consists in negotiating a protocol with the remote (through `multistream-select`), and applying //! consists in negotiating a protocol with the remote (through `multistream-select`), and applying
//! that protocol on the socket. //! that protocol on the socket.
//! //!
//! A potential connection upgrade is represented with the `ConnectionUpgrade` trait. The trait //! A potential connection upgrade is represented with the `ConnectionUpgrade` trait. The trait
//! consists in a protocol name plus a method that turns the socket into an `Output` object whose //! consists in a protocol name plus a method that turns the socket into an `Output` object whose
//! nature and type is specific to each upgrade. //! nature and type is specific to each upgrade.
//! //!
//! There exists three kinds of connection upgrades: middlewares, muxers, and actual protocols. //! There exists three kinds of connection upgrades: middlewares, muxers, and actual protocols.
//! //!
//! ## Middlewares //! ## Middlewares
//! //!
//! Examples of middleware connection upgrades include `PlainTextConfig` (dummy upgrade) or //! Examples of middleware connection upgrades include `PlainTextConfig` (dummy upgrade) or
//! `SecioConfig` (encyption layer, provided by the `secio` crate). //! `SecioConfig` (encyption layer, provided by the `secio` crate).
//! //!
//! The output of a middleware connection upgrade implements the `AsyncRead` and `AsyncWrite` //! The output of a middleware connection upgrade implements the `AsyncRead` and `AsyncWrite`
//! traits, just like sockets do. //! traits, just like sockets do.
//! //!
//! A middleware can be applied on a transport by using the `with_upgrade` method of the //! A middleware can be applied on a transport by using the `with_upgrade` method of the
//! `Transport` trait. The return value of this method also implements the `Transport` trait, which //! `Transport` trait. The return value of this method also implements the `Transport` trait, which
//! means that you can call `dial()` and `listen_on()` on it in order to directly obtain an //! means that you can call `dial()` and `listen_on()` on it in order to directly obtain an
@ -83,66 +83,66 @@
//! `next_incoming()` method will automatically apply the upgrade on both the dialer and the //! `next_incoming()` method will automatically apply the upgrade on both the dialer and the
//! listener. An error is produced if the remote doesn't support the protocol corresponding to the //! listener. An error is produced if the remote doesn't support the protocol corresponding to the
//! connection upgrade. //! connection upgrade.
//! //!
//! ``` //! ```
//! extern crate libp2p_swarm; //! extern crate libp2p_swarm;
//! extern crate libp2p_tcp_transport; //! extern crate libp2p_tcp_transport;
//! extern crate tokio_core; //! extern crate tokio_core;
//! //!
//! use libp2p_swarm::Transport; //! use libp2p_swarm::Transport;
//! //!
//! # fn main() { //! # fn main() {
//! let tokio_core = tokio_core::reactor::Core::new().unwrap(); //! let tokio_core = tokio_core::reactor::Core::new().unwrap();
//! let tcp_transport = libp2p_tcp_transport::TcpConfig::new(tokio_core.handle()); //! let tcp_transport = libp2p_tcp_transport::TcpConfig::new(tokio_core.handle());
//! let upgraded = tcp_transport.with_upgrade(libp2p_swarm::PlainTextConfig); //! let upgraded = tcp_transport.with_upgrade(libp2p_swarm::PlainTextConfig);
//! //!
//! // upgraded.dial(...) // automatically applies the plain text protocol on the socket //! // upgraded.dial(...) // automatically applies the plain text protocol on the socket
//! # } //! # }
//! ``` //! ```
//! //!
//! ## Muxers //! ## Muxers
//! //!
//! The concept of *muxing* consists in using a single stream as if it was multiple substreams. //! The concept of *muxing* consists in using a single stream as if it was multiple substreams.
//! //!
//! If the output of the connection upgrade instead implements the `StreamMuxer` and `Clone` //! If the output of the connection upgrade instead implements the `StreamMuxer` and `Clone`
//! traits, then you can turn the `UpgradedNode` struct into a `ConnectionReuse` struct by calling //! traits, then you can turn the `UpgradedNode` struct into a `ConnectionReuse` struct by calling
//! `ConnectionReuse::from(upgraded_node)`. //! `ConnectionReuse::from(upgraded_node)`.
//! //!
//! The `ConnectionReuse` struct then implements the `Transport` and `MuxedTransport` traits, and //! The `ConnectionReuse` struct then implements the `Transport` and `MuxedTransport` traits, and
//! can be used to dial or listen to multiaddresses, just like any other transport. The only //! can be used to dial or listen to multiaddresses, just like any other transport. The only
//! difference is that dialing a node will try to open a new substream on an existing connection //! difference is that dialing a node will try to open a new substream on an existing connection
//! instead of opening a new one every time. //! instead of opening a new one every time.
//! //!
//! > **Note**: Right now the `ConnectionReuse` struct is not fully implemented. //! > **Note**: Right now the `ConnectionReuse` struct is not fully implemented.
//! //!
//! TODO: add an example once the multiplex pull request is merged //! TODO: add an example once the multiplex pull request is merged
//! //!
//! ## Actual protocols //! ## Actual protocols
//! //!
//! *Actual protocols* work the same way as middlewares, except that their `Output` doesn't //! *Actual protocols* work the same way as middlewares, except that their `Output` doesn't
//! implement the `AsyncRead` and `AsyncWrite` traits. This means that that the return value of //! implement the `AsyncRead` and `AsyncWrite` traits. This means that that the return value of
//! `with_upgrade` does **not** implement the `Transport` trait and thus cannot be used as a //! `with_upgrade` does **not** implement the `Transport` trait and thus cannot be used as a
//! transport. //! transport.
//! //!
//! However the `UpgradedNode` struct returned by `with_upgrade` still provides methods named //! However the `UpgradedNode` struct returned by `with_upgrade` still provides methods named
//! `dial`, `listen_on`, and `next_incoming`, which will yield you a `Future` or a `Stream`, //! `dial`, `listen_on`, and `next_incoming`, which will yield you a `Future` or a `Stream`,
//! which you can use to obtain the `Output`. This `Output` can then be used in a protocol-specific //! which you can use to obtain the `Output`. This `Output` can then be used in a protocol-specific
//! way to use the protocol. //! way to use the protocol.
//! //!
//! ```no_run //! ```no_run
//! extern crate futures; //! extern crate futures;
//! extern crate libp2p_ping; //! extern crate libp2p_ping;
//! extern crate libp2p_swarm; //! extern crate libp2p_swarm;
//! extern crate libp2p_tcp_transport; //! extern crate libp2p_tcp_transport;
//! extern crate tokio_core; //! extern crate tokio_core;
//! //!
//! use futures::Future; //! use futures::Future;
//! use libp2p_ping::Ping; //! use libp2p_ping::Ping;
//! use libp2p_swarm::Transport; //! use libp2p_swarm::Transport;
//! //!
//! # fn main() { //! # fn main() {
//! let mut core = tokio_core::reactor::Core::new().unwrap(); //! let mut core = tokio_core::reactor::Core::new().unwrap();
//! //!
//! let ping_finished_future = libp2p_tcp_transport::TcpConfig::new(core.handle()) //! let ping_finished_future = libp2p_tcp_transport::TcpConfig::new(core.handle())
//! // We have a `TcpConfig` struct that implements `Transport`, and apply a `Ping` upgrade on it. //! // We have a `TcpConfig` struct that implements `Transport`, and apply a `Ping` upgrade on it.
//! .with_upgrade(Ping) //! .with_upgrade(Ping)
@ -152,14 +152,14 @@
//! .and_then(|((mut pinger, service), _)| { //! .and_then(|((mut pinger, service), _)| {
//! pinger.ping().map_err(|_| panic!()).select(service).map_err(|_| panic!()) //! pinger.ping().map_err(|_| panic!()).select(service).map_err(|_| panic!())
//! }); //! });
//! //!
//! // Runs until the ping arrives. //! // Runs until the ping arrives.
//! core.run(ping_finished_future).unwrap(); //! core.run(ping_finished_future).unwrap();
//! # } //! # }
//! ``` //! ```
//! //!
//! ## Grouping protocols //! ## Grouping protocols
//! //!
//! You can use the `.or_upgrade()` method to group multiple upgrades together. The return value //! You can use the `.or_upgrade()` method to group multiple upgrades together. The return value
//! also implements the `ConnectionUpgrade` trait and will choose one of the protocols amongst the //! also implements the `ConnectionUpgrade` trait and will choose one of the protocols amongst the
//! ones supported. //! ones supported.
@ -177,26 +177,26 @@
//! extern crate libp2p_swarm; //! extern crate libp2p_swarm;
//! extern crate libp2p_tcp_transport; //! extern crate libp2p_tcp_transport;
//! extern crate tokio_core; //! extern crate tokio_core;
//! //!
//! use futures::Future; //! use futures::Future;
//! use libp2p_ping::Ping; //! use libp2p_ping::Ping;
//! use libp2p_swarm::Transport; //! use libp2p_swarm::Transport;
//! //!
//! # fn main() { //! # fn main() {
//! let mut core = tokio_core::reactor::Core::new().unwrap(); //! let mut core = tokio_core::reactor::Core::new().unwrap();
//! //!
//! let transport = libp2p_tcp_transport::TcpConfig::new(core.handle()) //! let transport = libp2p_tcp_transport::TcpConfig::new(core.handle())
//! .with_dummy_muxing(); //! .with_dummy_muxing();
//! //!
//! let (swarm_controller, swarm_future) = libp2p_swarm::swarm(transport, Ping, |(mut pinger, service), client_addr| { //! let (swarm_controller, swarm_future) = libp2p_swarm::swarm(transport, Ping, |(mut pinger, service), client_addr| {
//! pinger.ping().map_err(|_| panic!()) //! pinger.ping().map_err(|_| panic!())
//! .select(service).map_err(|_| panic!()) //! .select(service).map_err(|_| panic!())
//! .map(|_| ()) //! .map(|_| ())
//! }); //! });
//! //!
//! // The `swarm_controller` can then be used to do some operations. //! // The `swarm_controller` can then be used to do some operations.
//! swarm_controller.listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()); //! swarm_controller.listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap());
//! //!
//! // Runs until everything is finished. //! // Runs until everything is finished.
//! core.run(swarm_future).unwrap(); //! core.run(swarm_future).unwrap();
//! # } //! # }
@ -223,6 +223,6 @@ pub use self::connection_reuse::ConnectionReuse;
pub use self::multiaddr::Multiaddr; pub use self::multiaddr::Multiaddr;
pub use self::muxing::StreamMuxer; pub use self::muxing::StreamMuxer;
pub use self::swarm::{swarm, SwarmController, SwarmFuture}; pub use self::swarm::{swarm, SwarmController, SwarmFuture};
pub use self::transport::{ConnectionUpgrade, PlainTextConfig, Transport, UpgradedNode, OrUpgrade}; pub use self::transport::{ConnectionUpgrade, OrUpgrade, PlainTextConfig, Transport, UpgradedNode};
pub use self::transport::{Endpoint, SimpleProtocol, MuxedTransport, UpgradeExt}; pub use self::transport::{Endpoint, MuxedTransport, SimpleProtocol, UpgradeExt};
pub use self::transport::{DeniedConnectionUpgrade}; pub use self::transport::DeniedConnectionUpgrade;

View File

@ -27,17 +27,17 @@ use tokio_io::{AsyncRead, AsyncWrite};
/// > **Note**: The methods of this trait consume the object, but if the object implements `Clone` /// > **Note**: The methods of this trait consume the object, but if the object implements `Clone`
/// > then you can clone it and keep the original in order to open additional substreams. /// > then you can clone it and keep the original in order to open additional substreams.
pub trait StreamMuxer { pub trait StreamMuxer {
/// Type of the object that represents the raw substream where data can be read and written. /// Type of the object that represents the raw substream where data can be read and written.
type Substream: AsyncRead + AsyncWrite; type Substream: AsyncRead + AsyncWrite;
/// Future that will be resolved when a new incoming substream is open. /// Future that will be resolved when a new incoming substream is open.
type InboundSubstream: Future<Item = Self::Substream, Error = IoError>; type InboundSubstream: Future<Item = Self::Substream, Error = IoError>;
/// Future that will be resolved when the outgoing substream is open. /// Future that will be resolved when the outgoing substream is open.
type OutboundSubstream: Future<Item = Self::Substream, Error = IoError>; type OutboundSubstream: Future<Item = Self::Substream, Error = IoError>;
/// Produces a future that will be resolved when a new incoming substream arrives. /// Produces a future that will be resolved when a new incoming substream arrives.
fn inbound(self) -> Self::InboundSubstream; fn inbound(self) -> Self::InboundSubstream;
/// Opens a new outgoing substream, and produces a future that will be resolved when it becomes /// Opens a new outgoing substream, and produces a future that will be resolved when it becomes
/// available. /// available.
fn outbound(self) -> Self::OutboundSubstream; fn outbound(self) -> Self::OutboundSubstream;
} }

View File

@ -19,7 +19,7 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
use std::io::Error as IoError; use std::io::Error as IoError;
use futures::{IntoFuture, Future, Stream, Async, Poll, future}; use futures::{future, Async, Future, IntoFuture, Poll, Stream};
use futures::sync::mpsc; use futures::sync::mpsc;
use {ConnectionUpgrade, Multiaddr, MuxedTransport, UpgradedNode}; use {ConnectionUpgrade, Multiaddr, MuxedTransport, UpgradedNode};
@ -31,13 +31,17 @@ use {ConnectionUpgrade, Multiaddr, MuxedTransport, UpgradedNode};
/// Produces a `SwarmController` and an implementation of `Future`. The controller can be used to /// Produces a `SwarmController` and an implementation of `Future`. The controller can be used to
/// control, and the `Future` must be driven to completion in order for things to work. /// control, and the `Future` must be driven to completion in order for things to work.
/// ///
pub fn swarm<T, C, H, F>(transport: T, upgrade: C, handler: H) pub fn swarm<T, C, H, F>(
-> (SwarmController<T, C>, SwarmFuture<T, C, H, F::Future>) transport: T,
where T: MuxedTransport + Clone + 'static, // TODO: 'static :-/ upgrade: C,
C: ConnectionUpgrade<T::RawConn> + Clone + 'static, // TODO: 'static :-/ handler: H,
C::NamesIter: Clone, // TODO: not elegant ) -> (SwarmController<T, C>, SwarmFuture<T, C, H, F::Future>)
H: FnMut(C::Output, Multiaddr) -> F, where
F: IntoFuture<Item = (), Error = IoError>, T: MuxedTransport + Clone + 'static, // TODO: 'static :-/
C: ConnectionUpgrade<T::RawConn> + Clone + 'static, // TODO: 'static :-/
C::NamesIter: Clone, // TODO: not elegant
H: FnMut(C::Output, Multiaddr) -> F,
F: IntoFuture<Item = (), Error = IoError>,
{ {
let (new_dialers_tx, new_dialers_rx) = mpsc::unbounded(); let (new_dialers_tx, new_dialers_rx) = mpsc::unbounded();
let (new_listeners_tx, new_listeners_rx) = mpsc::unbounded(); let (new_listeners_tx, new_listeners_rx) = mpsc::unbounded();
@ -71,40 +75,53 @@ pub fn swarm<T, C, H, F>(transport: T, upgrade: C, handler: H)
/// Allows control of what the swarm is doing. /// Allows control of what the swarm is doing.
pub struct SwarmController<T, C> pub struct SwarmController<T, C>
where T: MuxedTransport + 'static, // TODO: 'static :-/ where
C: ConnectionUpgrade<T::RawConn> + 'static, // TODO: 'static :-/ T: MuxedTransport + 'static, // TODO: 'static :-/
C: ConnectionUpgrade<T::RawConn> + 'static, // TODO: 'static :-/
{ {
transport: T, transport: T,
upgraded: UpgradedNode<T, C>, upgraded: UpgradedNode<T, C>,
new_listeners: mpsc::UnboundedSender<Box<Stream<Item = Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>, Error = IoError>>>, new_listeners: mpsc::UnboundedSender<
Box<
Stream<
Item = Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>,
Error = IoError,
>,
>,
>,
new_dialers: mpsc::UnboundedSender<Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>>, new_dialers: mpsc::UnboundedSender<Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>>,
new_toprocess: mpsc::UnboundedSender<Box<Future<Item = (), Error = IoError>>>, new_toprocess: mpsc::UnboundedSender<Box<Future<Item = (), Error = IoError>>>,
} }
impl<T, C> SwarmController<T, C> impl<T, C> SwarmController<T, C>
where T: MuxedTransport + Clone + 'static, // TODO: 'static :-/ where
C: ConnectionUpgrade<T::RawConn> + Clone + 'static, // TODO: 'static :-/ T: MuxedTransport + Clone + 'static, // TODO: 'static :-/
C::NamesIter: Clone, // TODO: not elegant C: ConnectionUpgrade<T::RawConn> + Clone + 'static, // TODO: 'static :-/
C::NamesIter: Clone, // TODO: not elegant
{ {
/// Asks the swarm to dial the node with the given multiaddress. The connection is then /// Asks the swarm to dial the node with the given multiaddress. The connection is then
/// upgraded using the `upgrade`, and the output is sent to the handler that was passed when /// upgraded using the `upgrade`, and the output is sent to the handler that was passed when
/// calling `swarm`. /// calling `swarm`.
// TODO: consider returning a future so that errors can be processed? // TODO: consider returning a future so that errors can be processed?
pub fn dial_to_handler<Du>(&self, multiaddr: Multiaddr, upgrade: Du) -> Result<(), Multiaddr> pub fn dial_to_handler<Du>(&self, multiaddr: Multiaddr, upgrade: Du) -> Result<(), Multiaddr>
where Du: ConnectionUpgrade<T::RawConn> + Clone + 'static, // TODO: 'static :-/ where
Du::Output: Into<C::Output>, Du: ConnectionUpgrade<T::RawConn> + Clone + 'static, // TODO: 'static :-/
Du::Output: Into<C::Output>,
{ {
match self.transport.clone().with_upgrade(upgrade).dial(multiaddr.clone()) { match self.transport
.clone()
.with_upgrade(upgrade)
.dial(multiaddr.clone())
{
Ok(dial) => { Ok(dial) => {
let dial = Box::new(dial.map(|(d, client_addr)| (d.into(), client_addr))) as Box<Future<Item = _, Error = _>>; let dial = Box::new(dial.map(|(d, client_addr)| (d.into(), client_addr)))
as Box<Future<Item = _, Error = _>>;
// Ignoring errors if the receiver has been closed, because in that situation // Ignoring errors if the receiver has been closed, because in that situation
// nothing is going to be processed anyway. // nothing is going to be processed anyway.
let _ = self.new_dialers.unbounded_send(dial); let _ = self.new_dialers.unbounded_send(dial);
Ok(()) Ok(())
}, }
Err((_, multiaddr)) => { Err((_, multiaddr)) => Err(multiaddr),
Err(multiaddr)
},
} }
} }
@ -114,11 +131,16 @@ impl<T, C> SwarmController<T, C>
/// Contrary to `dial_to_handler`, the output of the upgrade is not given to the handler that /// Contrary to `dial_to_handler`, the output of the upgrade is not given to the handler that
/// was passed at initialization. /// was passed at initialization.
// TODO: consider returning a future so that errors can be processed? // TODO: consider returning a future so that errors can be processed?
pub fn dial_custom_handler<Du, Df, Dfu>(&self, multiaddr: Multiaddr, upgrade: Du, and_then: Df) pub fn dial_custom_handler<Du, Df, Dfu>(
-> Result<(), Multiaddr> &self,
where Du: ConnectionUpgrade<T::RawConn> + 'static, // TODO: 'static :-/ multiaddr: Multiaddr,
Df: FnOnce(Du::Output, Multiaddr) -> Dfu + 'static, // TODO: 'static :-/ upgrade: Du,
Dfu: IntoFuture<Item = (), Error = IoError> + 'static, // TODO: 'static :-/ and_then: Df,
) -> Result<(), Multiaddr>
where
Du: ConnectionUpgrade<T::RawConn> + 'static, // TODO: 'static :-/
Df: FnOnce(Du::Output, Multiaddr) -> Dfu + 'static, // TODO: 'static :-/
Dfu: IntoFuture<Item = (), Error = IoError> + 'static, // TODO: 'static :-/
{ {
match self.transport.clone().with_upgrade(upgrade).dial(multiaddr) { match self.transport.clone().with_upgrade(upgrade).dial(multiaddr) {
Ok(dial) => { Ok(dial) => {
@ -127,10 +149,8 @@ impl<T, C> SwarmController<T, C>
// nothing is going to be processed anyway. // nothing is going to be processed anyway.
let _ = self.new_toprocess.unbounded_send(dial); let _ = self.new_toprocess.unbounded_send(dial);
Ok(()) Ok(())
}, }
Err((_, multiaddr)) => { Err((_, multiaddr)) => Err(multiaddr),
Err(multiaddr)
},
} }
} }
@ -143,38 +163,55 @@ impl<T, C> SwarmController<T, C>
// nothing is going to be processed anyway. // nothing is going to be processed anyway.
let _ = self.new_listeners.unbounded_send(listener); let _ = self.new_listeners.unbounded_send(listener);
Ok(new_addr) Ok(new_addr)
}, }
Err((_, multiaddr)) => { Err((_, multiaddr)) => Err(multiaddr),
Err(multiaddr)
},
} }
} }
} }
/// Future that must be driven to completion in order for the swarm to work. /// Future that must be driven to completion in order for the swarm to work.
pub struct SwarmFuture<T, C, H, F> pub struct SwarmFuture<T, C, H, F>
where T: MuxedTransport + 'static, // TODO: 'static :-/ where
C: ConnectionUpgrade<T::RawConn> + 'static, // TODO: 'static :-/ T: MuxedTransport + 'static, // TODO: 'static :-/
C: ConnectionUpgrade<T::RawConn> + 'static, // TODO: 'static :-/
{ {
upgraded: UpgradedNode<T, C>, upgraded: UpgradedNode<T, C>,
handler: H, handler: H,
new_listeners: mpsc::UnboundedReceiver<Box<Stream<Item = Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>, Error = IoError>>>, new_listeners: mpsc::UnboundedReceiver<
next_incoming: Box<Future<Item = Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>, Error = IoError>>, Box<
listeners: Vec<Box<Stream<Item = Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>, Error = IoError>>>, Stream<
Item = Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>,
Error = IoError,
>,
>,
>,
next_incoming: Box<
Future<Item = Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>, Error = IoError>,
>,
listeners: Vec<
Box<
Stream<
Item = Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>,
Error = IoError,
>,
>,
>,
listeners_upgrade: Vec<Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>>, listeners_upgrade: Vec<Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>>,
dialers: Vec<Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>>, dialers: Vec<Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>>,
new_dialers: mpsc::UnboundedReceiver<Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>>, new_dialers:
mpsc::UnboundedReceiver<Box<Future<Item = (C::Output, Multiaddr), Error = IoError>>>,
to_process: Vec<future::Either<F, Box<Future<Item = (), Error = IoError>>>>, to_process: Vec<future::Either<F, Box<Future<Item = (), Error = IoError>>>>,
new_toprocess: mpsc::UnboundedReceiver<Box<Future<Item = (), Error = IoError>>>, new_toprocess: mpsc::UnboundedReceiver<Box<Future<Item = (), Error = IoError>>>,
} }
impl<T, C, H, If, F> Future for SwarmFuture<T, C, H, F> impl<T, C, H, If, F> Future for SwarmFuture<T, C, H, F>
where T: MuxedTransport + Clone + 'static, // TODO: 'static :-/, where
C: ConnectionUpgrade<T::RawConn> + Clone + 'static, // TODO: 'static :-/ T: MuxedTransport + Clone + 'static, // TODO: 'static :-/,
C::NamesIter: Clone, // TODO: not elegant C: ConnectionUpgrade<T::RawConn> + Clone + 'static, // TODO: 'static :-/
H: FnMut(C::Output, Multiaddr) -> If, C::NamesIter: Clone, // TODO: not elegant
If: IntoFuture<Future = F, Item = (), Error = IoError>, H: FnMut(C::Output, Multiaddr) -> If,
F: Future<Item = (), Error = IoError>, If: IntoFuture<Future = F, Item = (), Error = IoError>,
F: Future<Item = (), Error = IoError>,
{ {
type Item = (); type Item = ();
type Error = IoError; type Error = IoError;
@ -186,91 +223,94 @@ impl<T, C, H, If, F> Future for SwarmFuture<T, C, H, F>
Ok(Async::Ready(connec)) => { Ok(Async::Ready(connec)) => {
self.next_incoming = self.upgraded.clone().next_incoming(); self.next_incoming = self.upgraded.clone().next_incoming();
self.listeners_upgrade.push(connec); self.listeners_upgrade.push(connec);
}, }
Ok(Async::NotReady) => {}, Ok(Async::NotReady) => {}
// TODO: may not be the best idea because we're killing the whole server // TODO: may not be the best idea because we're killing the whole server
Err(_err) => { Err(_err) => {
self.next_incoming = self.upgraded.clone().next_incoming(); self.next_incoming = self.upgraded.clone().next_incoming();
}, }
}; };
match self.new_listeners.poll() { match self.new_listeners.poll() {
Ok(Async::Ready(Some(new_listener))) => { Ok(Async::Ready(Some(new_listener))) => {
self.listeners.push(new_listener); self.listeners.push(new_listener);
}, }
Ok(Async::Ready(None)) | Err(_) => { Ok(Async::Ready(None)) | Err(_) => {
// New listener sender has been closed. // New listener sender has been closed.
}, }
Ok(Async::NotReady) => {}, Ok(Async::NotReady) => {}
}; };
match self.new_dialers.poll() { match self.new_dialers.poll() {
Ok(Async::Ready(Some(new_dialer))) => { Ok(Async::Ready(Some(new_dialer))) => {
self.dialers.push(new_dialer); self.dialers.push(new_dialer);
}, }
Ok(Async::Ready(None)) | Err(_) => { Ok(Async::Ready(None)) | Err(_) => {
// New dialers sender has been closed. // New dialers sender has been closed.
}, }
Ok(Async::NotReady) => {}, Ok(Async::NotReady) => {}
}; };
match self.new_toprocess.poll() { match self.new_toprocess.poll() {
Ok(Async::Ready(Some(new_toprocess))) => { Ok(Async::Ready(Some(new_toprocess))) => {
self.to_process.push(future::Either::B(new_toprocess)); self.to_process.push(future::Either::B(new_toprocess));
}, }
Ok(Async::Ready(None)) | Err(_) => { Ok(Async::Ready(None)) | Err(_) => {
// New to-process sender has been closed. // New to-process sender has been closed.
}, }
Ok(Async::NotReady) => {}, Ok(Async::NotReady) => {}
}; };
for n in (0 .. self.listeners.len()).rev() { for n in (0..self.listeners.len()).rev() {
let mut listener = self.listeners.swap_remove(n); let mut listener = self.listeners.swap_remove(n);
match listener.poll() { match listener.poll() {
Ok(Async::Ready(Some(upgrade))) => { Ok(Async::Ready(Some(upgrade))) => {
self.listeners.push(listener); self.listeners.push(listener);
self.listeners_upgrade.push(upgrade); self.listeners_upgrade.push(upgrade);
}, }
Ok(Async::NotReady) => { Ok(Async::NotReady) => {
self.listeners.push(listener); self.listeners.push(listener);
}, }
Ok(Async::Ready(None)) => {}, Ok(Async::Ready(None)) => {}
Err(_err) => {}, // Ignoring errors Err(_err) => {} // Ignoring errors
}; };
} }
for n in (0 .. self.listeners_upgrade.len()).rev() { for n in (0..self.listeners_upgrade.len()).rev() {
let mut upgrade = self.listeners_upgrade.swap_remove(n); let mut upgrade = self.listeners_upgrade.swap_remove(n);
match upgrade.poll() { match upgrade.poll() {
Ok(Async::Ready((output, client_addr))) => { Ok(Async::Ready((output, client_addr))) => {
self.to_process.push(future::Either::A(handler(output, client_addr).into_future())); self.to_process.push(future::Either::A(
}, handler(output, client_addr).into_future(),
));
}
Ok(Async::NotReady) => { Ok(Async::NotReady) => {
self.listeners_upgrade.push(upgrade); self.listeners_upgrade.push(upgrade);
}, }
Err(_err) => {}, // Ignoring errors Err(_err) => {} // Ignoring errors
} }
} }
for n in (0 .. self.dialers.len()).rev() { for n in (0..self.dialers.len()).rev() {
let mut dialer = self.dialers.swap_remove(n); let mut dialer = self.dialers.swap_remove(n);
match dialer.poll() { match dialer.poll() {
Ok(Async::Ready((output, addr))) => { Ok(Async::Ready((output, addr))) => {
self.to_process.push(future::Either::A(handler(output, addr).into_future())); self.to_process
}, .push(future::Either::A(handler(output, addr).into_future()));
}
Ok(Async::NotReady) => { Ok(Async::NotReady) => {
self.dialers.push(dialer); self.dialers.push(dialer);
}, }
Err(_err) => {}, // Ignoring errors Err(_err) => {} // Ignoring errors
} }
} }
for n in (0 .. self.to_process.len()).rev() { for n in (0..self.to_process.len()).rev() {
let mut to_process = self.to_process.swap_remove(n); let mut to_process = self.to_process.swap_remove(n);
match to_process.poll() { match to_process.poll() {
Ok(Async::Ready(())) => {}, Ok(Async::Ready(())) => {}
Ok(Async::NotReady) => self.to_process.push(to_process), Ok(Async::NotReady) => self.to_process.push(to_process),
Err(_err) => {}, // Ignoring errors Err(_err) => {} // Ignoring errors
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -28,8 +28,8 @@ extern crate tokio_io;
use bytes::BytesMut; use bytes::BytesMut;
use futures::future::Future; use futures::future::Future;
use futures::{Stream, Sink}; use futures::{Sink, Stream};
use libp2p_swarm::{Multiaddr, Transport, StreamMuxer, MuxedTransport}; use libp2p_swarm::{Multiaddr, MuxedTransport, StreamMuxer, Transport};
use libp2p_tcp_transport::TcpConfig; use libp2p_tcp_transport::TcpConfig;
use tokio_core::reactor::Core; use tokio_core::reactor::Core;
use tokio_io::codec::length_delimited::Framed; use tokio_io::codec::length_delimited::Framed;
@ -46,7 +46,10 @@ impl<T> From<T> for OnlyOnce<T> {
} }
impl<T: Clone> Clone for OnlyOnce<T> { impl<T: Clone> Clone for OnlyOnce<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
OnlyOnce(self.0.clone(), atomic::AtomicBool::new(self.1.load(atomic::Ordering::SeqCst))) OnlyOnce(
self.0.clone(),
atomic::AtomicBool::new(self.1.load(atomic::Ordering::SeqCst)),
)
} }
} }
impl<T: Transport> Transport for OnlyOnce<T> { impl<T: Transport> Transport for OnlyOnce<T> {
@ -80,7 +83,8 @@ fn client_to_server_outbound() {
.into_connection_reuse(); .into_connection_reuse();
let (listener, addr) = transport let (listener, addr) = transport
.listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap()).unwrap_or_else(|_| panic!()); .listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap())
.unwrap_or_else(|_| panic!());
tx.send(addr).unwrap(); tx.send(addr).unwrap();
let future = listener let future = listener
@ -90,7 +94,8 @@ fn client_to_server_outbound() {
.map(|client| client.0) .map(|client| client.0)
.map(|client| Framed::<_, BytesMut>::new(client)) .map(|client| Framed::<_, BytesMut>::new(client))
.and_then(|client| { .and_then(|client| {
client.into_future() client
.into_future()
.map_err(|(err, _)| err) .map_err(|(err, _)| err)
.map(|(msg, _)| msg) .map(|(msg, _)| msg)
}) })
@ -104,10 +109,11 @@ fn client_to_server_outbound() {
}); });
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let transport = TcpConfig::new(core.handle()) let transport = TcpConfig::new(core.handle()).with_upgrade(multiplex::MultiplexConfig);
.with_upgrade(multiplex::MultiplexConfig);
let future = transport.dial(rx.recv().unwrap()).unwrap() let future = transport
.dial(rx.recv().unwrap())
.unwrap()
.and_then(|client| client.0.outbound()) .and_then(|client| client.0.outbound())
.map(|server| Framed::<_, BytesMut>::new(server)) .map(|server| Framed::<_, BytesMut>::new(server))
.and_then(|server| server.send("hello world".into())) .and_then(|server| server.send("hello world".into()))
@ -131,7 +137,8 @@ fn connection_reused_for_dialing() {
.into_connection_reuse(); .into_connection_reuse();
let (listener, addr) = transport let (listener, addr) = transport
.listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap()).unwrap_or_else(|_| panic!()); .listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap())
.unwrap_or_else(|_| panic!());
tx.send(addr).unwrap(); tx.send(addr).unwrap();
let future = listener let future = listener
@ -140,7 +147,8 @@ fn connection_reused_for_dialing() {
.and_then(|(client, rest)| client.unwrap().map(move |c| (c.0, rest))) .and_then(|(client, rest)| client.unwrap().map(move |c| (c.0, rest)))
.map(|(client, rest)| (Framed::<_, BytesMut>::new(client), rest)) .map(|(client, rest)| (Framed::<_, BytesMut>::new(client), rest))
.and_then(|(client, rest)| { .and_then(|(client, rest)| {
client.into_future() client
.into_future()
.map(|v| (v, rest)) .map(|v| (v, rest))
.map_err(|(err, _)| err) .map_err(|(err, _)| err)
}) })
@ -155,10 +163,7 @@ fn connection_reused_for_dialing() {
.and_then(|(client, _)| client.unwrap()) .and_then(|(client, _)| client.unwrap())
.map(|client| client.0) .map(|client| client.0)
.map(|client| Framed::<_, BytesMut>::new(client)) .map(|client| Framed::<_, BytesMut>::new(client))
.and_then(|client| { .and_then(|client| client.into_future().map_err(|(err, _)| err))
client.into_future()
.map_err(|(err, _)| err)
})
.and_then(|(msg, _)| { .and_then(|(msg, _)| {
let msg = msg.unwrap(); let msg = msg.unwrap();
assert_eq!(msg, "second message"); assert_eq!(msg, "second message");
@ -175,19 +180,21 @@ fn connection_reused_for_dialing() {
let listen_addr = rx.recv().unwrap(); let listen_addr = rx.recv().unwrap();
let future = transport.clone().dial(listen_addr.clone()).unwrap_or_else(|_| panic!()) let future = transport
.clone()
.dial(listen_addr.clone())
.unwrap_or_else(|_| panic!())
.map(|server| Framed::<_, BytesMut>::new(server.0)) .map(|server| Framed::<_, BytesMut>::new(server.0))
.and_then(|server| { .and_then(|server| server.send("hello world".into()))
server.send("hello world".into())
})
.and_then(|first_connec| { .and_then(|first_connec| {
transport.clone().dial(listen_addr.clone()).unwrap_or_else(|_| panic!()) transport
.clone()
.dial(listen_addr.clone())
.unwrap_or_else(|_| panic!())
.map(|server| Framed::<_, BytesMut>::new(server.0)) .map(|server| Framed::<_, BytesMut>::new(server.0))
.map(|server| (first_connec, server)) .map(|server| (first_connec, server))
}) })
.and_then(|(_first, second)| { .and_then(|(_first, second)| second.send("second message".into()))
second.send("second message".into())
})
.map(|_| ()); .map(|_| ());
core.run(future).unwrap(); core.run(future).unwrap();
@ -204,11 +211,13 @@ fn use_opened_listen_to_dial() {
let bg_thread = thread::spawn(move || { let bg_thread = thread::spawn(move || {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let transport = OnlyOnce::from(TcpConfig::new(core.handle())) let transport =
.with_upgrade(multiplex::MultiplexConfig); OnlyOnce::from(TcpConfig::new(core.handle())).with_upgrade(multiplex::MultiplexConfig);
let (listener, addr) = transport.clone() let (listener, addr) = transport
.listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap()).unwrap_or_else(|_| panic!()); .clone()
.listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap())
.unwrap_or_else(|_| panic!());
tx.send(addr).unwrap(); tx.send(addr).unwrap();
let future = listener let future = listener
@ -220,11 +229,10 @@ fn use_opened_listen_to_dial() {
let c2 = c.clone(); let c2 = c.clone();
c.clone().inbound().map(move |i| (c2, i)) c.clone().inbound().map(move |i| (c2, i))
}) })
.map(|(muxer, client)| { .map(|(muxer, client)| (muxer, Framed::<_, BytesMut>::new(client)))
(muxer, Framed::<_, BytesMut>::new(client))
})
.and_then(|(muxer, client)| { .and_then(|(muxer, client)| {
client.into_future() client
.into_future()
.map(move |msg| (muxer, msg)) .map(move |msg| (muxer, msg))
.map_err(|(err, _)| err) .map_err(|(err, _)| err)
}) })
@ -233,13 +241,8 @@ fn use_opened_listen_to_dial() {
assert_eq!(msg, "hello world"); assert_eq!(msg, "hello world");
muxer.outbound() muxer.outbound()
}) })
.map(|client| { .map(|client| Framed::<_, BytesMut>::new(client))
Framed::<_, BytesMut>::new(client) .and_then(|client| client.into_future().map_err(|(err, _)| err))
})
.and_then(|client| {
client.into_future()
.map_err(|(err, _)| err)
})
.and_then(|(msg, _)| { .and_then(|(msg, _)| {
let msg = msg.unwrap(); let msg = msg.unwrap();
assert_eq!(msg, "second message"); assert_eq!(msg, "second message");
@ -256,20 +259,21 @@ fn use_opened_listen_to_dial() {
let listen_addr = rx.recv().unwrap(); let listen_addr = rx.recv().unwrap();
let future = transport.clone().dial(listen_addr.clone()).unwrap_or_else(|_| panic!()) let future = transport
.clone()
.dial(listen_addr.clone())
.unwrap_or_else(|_| panic!())
.map(|server| Framed::<_, BytesMut>::new(server.0)) .map(|server| Framed::<_, BytesMut>::new(server.0))
.and_then(|server| { .and_then(|server| server.send("hello world".into()))
server.send("hello world".into())
})
.and_then(|first_connec| { .and_then(|first_connec| {
transport.clone().next_incoming() transport
.clone()
.next_incoming()
.and_then(|server| server) .and_then(|server| server)
.map(|server| Framed::<_, BytesMut>::new(server.0)) .map(|server| Framed::<_, BytesMut>::new(server.0))
.map(|server| (first_connec, server)) .map(|server| (first_connec, server))
}) })
.and_then(|(_first, second)| { .and_then(|(_first, second)| second.send("second message".into()))
second.send("second message".into())
})
.map(|_| ()); .map(|_| ());
core.run(future).unwrap(); core.run(future).unwrap();

View File

@ -1,21 +1,21 @@
// Copyright 2017 Parity Technologies (UK) Ltd. // Copyright 2017 Parity Technologies (UK) Ltd.
// //
// Permission is hereby granted, free of charge, to any person obtaining a // Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"), // copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation // to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, // the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the // and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions: // Software is furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
// TODO: use this once stable ; for now we just copy-paste the content of the README.md // TODO: use this once stable ; for now we just copy-paste the content of the README.md
@ -24,9 +24,9 @@
//! Implementation of the libp2p `Transport` trait for TCP/IP. //! Implementation of the libp2p `Transport` trait for TCP/IP.
//! //!
//! Uses [the *tokio* library](https://tokio.rs). //! Uses [the *tokio* library](https://tokio.rs).
//! //!
//! # Usage //! # Usage
//! //!
//! Create [a tokio `Core`](https://docs.rs/tokio-core/0.1/tokio_core/reactor/struct.Core.html), //! Create [a tokio `Core`](https://docs.rs/tokio-core/0.1/tokio_core/reactor/struct.Core.html),
//! then grab a handle by calling the `handle()` method on it, then create a `TcpConfig` and pass //! then grab a handle by calling the `handle()` method on it, then create a `TcpConfig` and pass
//! the handle. //! the handle.
@ -49,20 +49,20 @@
//! The `TcpConfig` structs implements the `Transport` trait of the `swarm` library. See the //! The `TcpConfig` structs implements the `Transport` trait of the `swarm` library. See the
//! documentation of `swarm` and of libp2p in general to learn how to use the `Transport` trait. //! documentation of `swarm` and of libp2p in general to learn how to use the `Transport` trait.
extern crate futures;
extern crate libp2p_swarm as swarm; extern crate libp2p_swarm as swarm;
extern crate multiaddr;
extern crate tokio_core; extern crate tokio_core;
extern crate tokio_io; extern crate tokio_io;
extern crate multiaddr;
extern crate futures;
use std::io::Error as IoError; use std::io::Error as IoError;
use std::iter; use std::iter;
use std::net::SocketAddr; use std::net::SocketAddr;
use tokio_core::reactor::Handle; use tokio_core::reactor::Handle;
use tokio_core::net::{TcpStream, TcpListener}; use tokio_core::net::{TcpListener, TcpStream};
use futures::future::{self, Future, FutureResult, IntoFuture}; use futures::future::{self, Future, FutureResult, IntoFuture};
use futures::stream::Stream; use futures::stream::Stream;
use multiaddr::{Multiaddr, AddrComponent, ToMultiaddr}; use multiaddr::{AddrComponent, Multiaddr, ToMultiaddr};
use swarm::Transport; use swarm::Transport;
/// Represents the configuration for a TCP/IP transport capability for libp2p. /// Represents the configuration for a TCP/IP transport capability for libp2p.
@ -99,15 +99,18 @@ impl Transport for TcpConfig {
// just return the original multiaddr. // just return the original multiaddr.
let new_addr = match listener { let new_addr = match listener {
Ok(ref l) => if let Ok(new_s_addr) = l.local_addr() { Ok(ref l) => if let Ok(new_s_addr) = l.local_addr() {
new_s_addr.to_multiaddr().expect("multiaddr generated from socket addr is \ new_s_addr.to_multiaddr().expect(
always valid") "multiaddr generated from socket addr is \
always valid",
)
} else { } else {
addr addr
} },
Err(_) => addr, Err(_) => addr,
}; };
let future = future::result(listener).map(|listener| { let future = future::result(listener)
.map(|listener| {
// Pull out a stream of sockets for incoming connections // Pull out a stream of sockets for incoming connections
listener.incoming().map(|(sock, addr)| { listener.incoming().map(|(sock, addr)| {
let addr = addr.to_multiaddr() let addr = addr.to_multiaddr()
@ -115,7 +118,7 @@ impl Transport for TcpConfig {
Ok((sock, addr)).into_future() Ok((sock, addr)).into_future()
}) })
}) })
.flatten_stream(); .flatten_stream();
Ok((Box::new(future), new_addr)) Ok((Box::new(future), new_addr))
} else { } else {
Err((self, addr)) Err((self, addr))
@ -127,8 +130,7 @@ impl Transport for TcpConfig {
/// or gives back the multiaddress. /// or gives back the multiaddress.
fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> { fn dial(self, addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> {
if let Ok(socket_addr) = multiaddr_to_socketaddr(&addr) { if let Ok(socket_addr) = multiaddr_to_socketaddr(&addr) {
let fut = TcpStream::connect(&socket_addr, &self.event_loop) let fut = TcpStream::connect(&socket_addr, &self.event_loop).map(|t| (t, addr));
.map(|t| (t, addr));
Ok(Box::new(fut) as Box<_>) Ok(Box::new(fut) as Box<_>)
} else { } else {
Err((self, addr)) Err((self, addr))
@ -145,15 +147,15 @@ impl Transport for TcpConfig {
// Check that `server` is a valid TCP/IP address. // Check that `server` is a valid TCP/IP address.
match (&server_protocols[0], &server_protocols[1]) { match (&server_protocols[0], &server_protocols[1]) {
(&AddrComponent::IP4(_), &AddrComponent::TCP(_)) | (&AddrComponent::IP4(_), &AddrComponent::TCP(_))
(&AddrComponent::IP6(_), &AddrComponent::TCP(_)) => {} | (&AddrComponent::IP6(_), &AddrComponent::TCP(_)) => {}
_ => return None, _ => return None,
} }
// Check that `observed` is a valid TCP/IP address. // Check that `observed` is a valid TCP/IP address.
match (&observed_protocols[0], &observed_protocols[1]) { match (&observed_protocols[0], &observed_protocols[1]) {
(&AddrComponent::IP4(_), &AddrComponent::TCP(_)) | (&AddrComponent::IP4(_), &AddrComponent::TCP(_))
(&AddrComponent::IP6(_), &AddrComponent::TCP(_)) => {} | (&AddrComponent::IP6(_), &AddrComponent::TCP(_)) => {}
_ => return None, _ => return None,
} }
@ -186,7 +188,7 @@ fn multiaddr_to_socketaddr(addr: &Multiaddr) -> Result<SocketAddr, ()> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{TcpConfig, multiaddr_to_socketaddr}; use super::{multiaddr_to_socketaddr, TcpConfig};
use std; use std;
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use tokio_core::reactor::Core; use tokio_core::reactor::Core;
@ -200,7 +202,10 @@ mod tests {
fn multiaddr_to_tcp_conversion() { fn multiaddr_to_tcp_conversion() {
use std::net::Ipv6Addr; use std::net::Ipv6Addr;
assert!(multiaddr_to_socketaddr(&"/ip4/127.0.0.1/udp/1234".parse::<Multiaddr>().unwrap()).is_err()); assert!(
multiaddr_to_socketaddr(&"/ip4/127.0.0.1/udp/1234".parse::<Multiaddr>().unwrap())
.is_err()
);
assert_eq!( assert_eq!(
multiaddr_to_socketaddr(&"/ip4/127.0.0.1/tcp/12345".parse::<Multiaddr>().unwrap()), multiaddr_to_socketaddr(&"/ip4/127.0.0.1/tcp/12345".parse::<Multiaddr>().unwrap()),
@ -210,7 +215,9 @@ mod tests {
)) ))
); );
assert_eq!( assert_eq!(
multiaddr_to_socketaddr(&"/ip4/255.255.255.255/tcp/8080".parse::<Multiaddr>().unwrap()), multiaddr_to_socketaddr(&"/ip4/255.255.255.255/tcp/8080"
.parse::<Multiaddr>()
.unwrap()),
Ok(SocketAddr::new( Ok(SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)), IpAddr::V4(Ipv4Addr::new(255, 255, 255, 255)),
8080, 8080,
@ -225,7 +232,8 @@ mod tests {
); );
assert_eq!( assert_eq!(
multiaddr_to_socketaddr(&"/ip6/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/tcp/8080" multiaddr_to_socketaddr(&"/ip6/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/tcp/8080"
.parse::<Multiaddr>().unwrap()), .parse::<Multiaddr>()
.unwrap()),
Ok(SocketAddr::new( Ok(SocketAddr::new(
IpAddr::V6(Ipv6Addr::new( IpAddr::V6(Ipv6Addr::new(
65535, 65535,
@ -316,7 +324,9 @@ mod tests {
let core = Core::new().unwrap(); let core = Core::new().unwrap();
let tcp = TcpConfig::new(core.handle()); let tcp = TcpConfig::new(core.handle());
let addr = "/ip4/127.0.0.1/tcp/12345/tcp/12345".parse::<Multiaddr>().unwrap(); let addr = "/ip4/127.0.0.1/tcp/12345/tcp/12345"
.parse::<Multiaddr>()
.unwrap();
assert!(tcp.listen_on(addr).is_err()); assert!(tcp.listen_on(addr).is_err());
} }
@ -329,6 +339,9 @@ mod tests {
let observed = "/ip4/80.81.82.83/tcp/25000".parse::<Multiaddr>().unwrap(); let observed = "/ip4/80.81.82.83/tcp/25000".parse::<Multiaddr>().unwrap();
let out = tcp.nat_traversal(&server, &observed); let out = tcp.nat_traversal(&server, &observed);
assert_eq!(out.unwrap(), "/ip4/80.81.82.83/tcp/10000".parse::<Multiaddr>().unwrap()); assert_eq!(
out.unwrap(),
"/ip4/80.81.82.83/tcp/10000".parse::<Multiaddr>().unwrap()
);
} }
} }

View File

@ -40,305 +40,305 @@ use tokio_io::{AsyncRead, AsyncWrite};
pub struct BrowserWsConfig; pub struct BrowserWsConfig;
impl BrowserWsConfig { impl BrowserWsConfig {
/// Creates a new configuration object for websocket. /// Creates a new configuration object for websocket.
#[inline] #[inline]
pub fn new() -> BrowserWsConfig { pub fn new() -> BrowserWsConfig {
BrowserWsConfig BrowserWsConfig
} }
} }
impl Transport for BrowserWsConfig { impl Transport for BrowserWsConfig {
type RawConn = BrowserWsConn; type RawConn = BrowserWsConn;
type Listener = Box<Stream<Item = Self::ListenerUpgrade, Error = IoError>>; // TODO: use `!` type Listener = Box<Stream<Item = Self::ListenerUpgrade, Error = IoError>>; // TODO: use `!`
type ListenerUpgrade = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>; // TODO: use `!` type ListenerUpgrade = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>; // TODO: use `!`
type Dial = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>; type Dial = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>;
#[inline] #[inline]
fn listen_on(self, a: Multiaddr) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> { fn listen_on(self, a: Multiaddr) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> {
// Listening is never supported. // Listening is never supported.
Err((self, a)) Err((self, a))
} }
fn dial(self, original_addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> { fn dial(self, original_addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> {
// Making sure we are initialized before we dial. Initialization is protected by a simple // Making sure we are initialized before we dial. Initialization is protected by a simple
// boolean static variable, so it's not a problem to call it multiple times and the cost // boolean static variable, so it's not a problem to call it multiple times and the cost
// is negligible. // is negligible.
stdweb::initialize(); stdweb::initialize();
// Tries to interpret the multiaddr, and returns a corresponding `ws://x.x.x.x/` URL (as // Tries to interpret the multiaddr, and returns a corresponding `ws://x.x.x.x/` URL (as
// a string) on success. // a string) on success.
let inner_addr = match multiaddr_to_target(&original_addr) { let inner_addr = match multiaddr_to_target(&original_addr) {
Ok(a) => a, Ok(a) => a,
Err(_) => return Err((self, original_addr)), Err(_) => return Err((self, original_addr)),
}; };
// Create the JS `WebSocket` object. // Create the JS `WebSocket` object.
let websocket = { let websocket = {
let val = js! { let val = js! {
try { try {
return new WebSocket(@{inner_addr}); return new WebSocket(@{inner_addr});
} catch(e) { } catch(e) {
return false; return false;
} }
}; };
match val.into_reference() { match val.into_reference() {
Some(ws) => ws, Some(ws) => ws,
None => return Err((self, original_addr)), // `false` was returned by `js!` None => return Err((self, original_addr)), // `false` was returned by `js!`
} }
}; };
// Create a `message` channel that will be used for both bytes messages and errors, and a // Create a `message` channel that will be used for both bytes messages and errors, and a
// `message_cb` used for the `message` event on the WebSocket. // `message_cb` used for the `message` event on the WebSocket.
// `message_tx` is grabbed by `message_cb` and `close_cb`, and `message_rx` is grabbed // `message_tx` is grabbed by `message_cb` and `close_cb`, and `message_rx` is grabbed
// by `open_cb`. // by `open_cb`.
let (message_tx, message_rx) = mpsc::unbounded::<Result<Vec<u8>, IoError>>(); let (message_tx, message_rx) = mpsc::unbounded::<Result<Vec<u8>, IoError>>();
let message_tx = Arc::new(message_tx); let message_tx = Arc::new(message_tx);
let mut message_rx = Some(message_rx); let mut message_rx = Some(message_rx);
let message_cb = { let message_cb = {
let message_tx = message_tx.clone(); let message_tx = message_tx.clone();
move |message_data: Reference| { move |message_data: Reference| {
if let Some(buffer) = message_data.downcast::<TypedArray<u8>>() { if let Some(buffer) = message_data.downcast::<TypedArray<u8>>() {
let _ = message_tx.unbounded_send(Ok(buffer.to_vec())); let _ = message_tx.unbounded_send(Ok(buffer.to_vec()));
} else { } else {
let _ = message_tx.unbounded_send(Err(IoError::new( let _ = message_tx.unbounded_send(Err(IoError::new(
IoErrorKind::InvalidData, IoErrorKind::InvalidData,
"received ws message of unknown type", "received ws message of unknown type",
))); )));
} }
} }
}; };
// Create a `open` channel that will be used to communicate the `BrowserWsConn` that represents // Create a `open` channel that will be used to communicate the `BrowserWsConn` that represents
// the open dialing websocket. Also create a `open_cb` callback that will be used for the // the open dialing websocket. Also create a `open_cb` callback that will be used for the
// `open` message of the websocket. // `open` message of the websocket.
let (open_tx, open_rx) = oneshot::channel::<Result<BrowserWsConn, IoError>>(); let (open_tx, open_rx) = oneshot::channel::<Result<BrowserWsConn, IoError>>();
let open_tx = Arc::new(Mutex::new(Some(open_tx))); let open_tx = Arc::new(Mutex::new(Some(open_tx)));
let websocket_clone = websocket.clone(); let websocket_clone = websocket.clone();
let open_cb = { let open_cb = {
let open_tx = open_tx.clone(); let open_tx = open_tx.clone();
move || { move || {
// Note that `open_tx` can be empty (and a panic happens) if the `open` event // Note that `open_tx` can be empty (and a panic happens) if the `open` event
// is triggered twice, or is triggered after the `close` event. We never reuse the // is triggered twice, or is triggered after the `close` event. We never reuse the
// same websocket twice, so this is not supposed to happen. // same websocket twice, so this is not supposed to happen.
let tx = open_tx let tx = open_tx
.lock() .lock()
.unwrap() .unwrap()
.take() .take()
.expect("the websocket can only open once"); .expect("the websocket can only open once");
// `message_rx` can be empty if the `open` event is triggered twice, which again // `message_rx` can be empty if the `open` event is triggered twice, which again
// is not supposed to happen. // is not supposed to happen.
let message_rx = message_rx.take().expect("the websocket can only open once"); let message_rx = message_rx.take().expect("the websocket can only open once");
// Send a `BrowserWsConn` to the future that was returned by `dial`. Ignoring errors that // Send a `BrowserWsConn` to the future that was returned by `dial`. Ignoring errors that
// would happen the future has been dropped by the user. // would happen the future has been dropped by the user.
let _ = tx.send(Ok(BrowserWsConn { let _ = tx.send(Ok(BrowserWsConn {
websocket: websocket_clone.clone(), websocket: websocket_clone.clone(),
incoming_data: RwStreamSink::new(message_rx.then(|result| { incoming_data: RwStreamSink::new(message_rx.then(|result| {
// An `Err` happens here if `message_tx` has been dropped. However // An `Err` happens here if `message_tx` has been dropped. However
// `message_tx` is grabbed by the websocket, which stays alive for as // `message_tx` is grabbed by the websocket, which stays alive for as
// long as the `BrowserWsConn` is alive. // long as the `BrowserWsConn` is alive.
match result { match result {
Ok(r) => r, Ok(r) => r,
Err(_) => { Err(_) => {
unreachable!("the message channel outlives the BrowserWsConn") unreachable!("the message channel outlives the BrowserWsConn")
} }
} }
})), })),
})); }));
} }
}; };
// Used for the `close` message of the websocket. // Used for the `close` message of the websocket.
// The websocket can be closed either before or after being opened, so we send an error // The websocket can be closed either before or after being opened, so we send an error
// to both the `open` and `message` channels if that happens. // to both the `open` and `message` channels if that happens.
let close_cb = move || { let close_cb = move || {
if let Some(tx) = open_tx.lock().unwrap().take() { if let Some(tx) = open_tx.lock().unwrap().take() {
let _ = tx.send(Err(IoError::new( let _ = tx.send(Err(IoError::new(
IoErrorKind::ConnectionRefused, IoErrorKind::ConnectionRefused,
"close event on the websocket", "close event on the websocket",
))); )));
} }
let _ = message_tx.unbounded_send(Err(IoError::new( let _ = message_tx.unbounded_send(Err(IoError::new(
IoErrorKind::ConnectionRefused, IoErrorKind::ConnectionRefused,
"close event on the websocket", "close event on the websocket",
))); )));
}; };
js! { js! {
var socket = @{websocket}; var socket = @{websocket};
var open_cb = @{open_cb}; var open_cb = @{open_cb};
var message_cb = @{message_cb}; var message_cb = @{message_cb};
var close_cb = @{close_cb}; var close_cb = @{close_cb};
socket.addEventListener("open", function(event) { socket.addEventListener("open", function(event) {
open_cb(); open_cb();
}); });
socket.addEventListener("message", function(event) { socket.addEventListener("message", function(event) {
var reader = new FileReader(); var reader = new FileReader();
reader.addEventListener("loadend", function() { reader.addEventListener("loadend", function() {
var typed = new Uint8Array(reader.result); var typed = new Uint8Array(reader.result);
message_cb(typed); message_cb(typed);
}); });
reader.readAsArrayBuffer(event.data); reader.readAsArrayBuffer(event.data);
}); });
socket.addEventListener("close", function(event) { socket.addEventListener("close", function(event) {
close_cb(); close_cb();
}); });
}; };
Ok(Box::new(open_rx.then(|result| { Ok(Box::new(open_rx.then(|result| {
match result { match result {
Ok(Ok(r)) => Ok((r, original_addr)), Ok(Ok(r)) => Ok((r, original_addr)),
Ok(Err(e)) => Err(e), Ok(Err(e)) => Err(e),
// `Err` would happen here if `open_tx` is destroyed. `open_tx` is captured by // `Err` would happen here if `open_tx` is destroyed. `open_tx` is captured by
// the `WebSocket`, and the `WebSocket` is captured by `open_cb`, which is itself // the `WebSocket`, and the `WebSocket` is captured by `open_cb`, which is itself
// captured by the `WebSocket`. Due to this cyclic dependency, `open_tx` should // captured by the `WebSocket`. Due to this cyclic dependency, `open_tx` should
// never be destroyed. // never be destroyed.
// TODO: how do we break this cyclic dependency? difficult question // TODO: how do we break this cyclic dependency? difficult question
Err(_) => unreachable!("the sending side will only close when we drop the future"), Err(_) => unreachable!("the sending side will only close when we drop the future"),
} }
})) as Box<_>) })) as Box<_>)
} }
fn nat_traversal(&self, server: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> { fn nat_traversal(&self, server: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> {
let mut server_protocols = server.iter(); let mut server_protocols = server.iter();
let server_proto0 = server_protocols.next()?; let server_proto0 = server_protocols.next()?;
let server_proto1 = server_protocols.next()?; let server_proto1 = server_protocols.next()?;
let server_proto2 = server_protocols.next()?; let server_proto2 = server_protocols.next()?;
if server_protocols.next().is_some() { if server_protocols.next().is_some() {
return None; return None;
} }
let mut observed_protocols = observed.iter(); let mut observed_protocols = observed.iter();
let obs_proto0 = observed_protocols.next()?; let obs_proto0 = observed_protocols.next()?;
let obs_proto1 = observed_protocols.next()?; let obs_proto1 = observed_protocols.next()?;
let obs_proto2 = observed_protocols.next()?; let obs_proto2 = observed_protocols.next()?;
if observed_protocols.next().is_some() { if observed_protocols.next().is_some() {
return None; return None;
} }
// Check that `server` is a valid TCP/IP address. // Check that `server` is a valid TCP/IP address.
match (&server_proto0, &server_proto1, &server_proto2) { match (&server_proto0, &server_proto1, &server_proto2) {
(&AddrComponent::IP4(_), &AddrComponent::TCP(_), &AddrComponent::WS) | (&AddrComponent::IP4(_), &AddrComponent::TCP(_), &AddrComponent::WS)
(&AddrComponent::IP6(_), &AddrComponent::TCP(_), &AddrComponent::WS) | | (&AddrComponent::IP6(_), &AddrComponent::TCP(_), &AddrComponent::WS)
(&AddrComponent::IP4(_), &AddrComponent::TCP(_), &AddrComponent::WSS) | | (&AddrComponent::IP4(_), &AddrComponent::TCP(_), &AddrComponent::WSS)
(&AddrComponent::IP6(_), &AddrComponent::TCP(_), &AddrComponent::WSS) => {} | (&AddrComponent::IP6(_), &AddrComponent::TCP(_), &AddrComponent::WSS) => {}
_ => return None, _ => return None,
} }
// Check that `observed` is a valid TCP/IP address. // Check that `observed` is a valid TCP/IP address.
match (&obs_proto0, &obs_proto1, &obs_proto2) { match (&obs_proto0, &obs_proto1, &obs_proto2) {
(&AddrComponent::IP4(_), &AddrComponent::TCP(_), &AddrComponent::WS) | (&AddrComponent::IP4(_), &AddrComponent::TCP(_), &AddrComponent::WS)
(&AddrComponent::IP6(_), &AddrComponent::TCP(_), &AddrComponent::WS) | | (&AddrComponent::IP6(_), &AddrComponent::TCP(_), &AddrComponent::WS)
(&AddrComponent::IP4(_), &AddrComponent::TCP(_), &AddrComponent::WSS) | | (&AddrComponent::IP4(_), &AddrComponent::TCP(_), &AddrComponent::WSS)
(&AddrComponent::IP6(_), &AddrComponent::TCP(_), &AddrComponent::WSS) => {} | (&AddrComponent::IP6(_), &AddrComponent::TCP(_), &AddrComponent::WSS) => {}
_ => return None, _ => return None,
} }
// Note that it will still work if the server uses WSS while the client uses WS, // Note that it will still work if the server uses WSS while the client uses WS,
// or vice-versa. // or vice-versa.
let result = iter::once(obs_proto0) let result = iter::once(obs_proto0)
.chain(iter::once(server_proto1)) .chain(iter::once(server_proto1))
.chain(iter::once(server_proto2)) .chain(iter::once(server_proto2))
.collect(); .collect();
Some(result) Some(result)
} }
} }
pub struct BrowserWsConn { pub struct BrowserWsConn {
websocket: Reference, websocket: Reference,
// Stream of messages that goes through a `RwStreamSink` in order to become a `AsyncRead`. // Stream of messages that goes through a `RwStreamSink` in order to become a `AsyncRead`.
incoming_data: RwStreamSink< incoming_data: RwStreamSink<
StreamThen< StreamThen<
mpsc::UnboundedReceiver<Result<Vec<u8>, IoError>>, mpsc::UnboundedReceiver<Result<Vec<u8>, IoError>>,
fn(Result<Result<Vec<u8>, IoError>, ()>) -> Result<Vec<u8>, IoError>, fn(Result<Result<Vec<u8>, IoError>, ()>) -> Result<Vec<u8>, IoError>,
Result<Vec<u8>, IoError>, Result<Vec<u8>, IoError>,
>, >,
>, >,
} }
impl Drop for BrowserWsConn { impl Drop for BrowserWsConn {
#[inline] #[inline]
fn drop(&mut self) { fn drop(&mut self) {
// TODO: apparently there's a memory leak related to callbacks? // TODO: apparently there's a memory leak related to callbacks?
js! { @{&self.websocket}.close(); } js! { @{&self.websocket}.close(); }
} }
} }
impl AsyncRead for BrowserWsConn {} impl AsyncRead for BrowserWsConn {}
impl Read for BrowserWsConn { impl Read for BrowserWsConn {
#[inline] #[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> { fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
self.incoming_data.read(buf) self.incoming_data.read(buf)
} }
} }
impl AsyncWrite for BrowserWsConn { impl AsyncWrite for BrowserWsConn {
#[inline] #[inline]
fn shutdown(&mut self) -> Poll<(), IoError> { fn shutdown(&mut self) -> Poll<(), IoError> {
Ok(Async::Ready(())) Ok(Async::Ready(()))
} }
} }
impl Write for BrowserWsConn { impl Write for BrowserWsConn {
fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> { fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
let typed_array = TypedArray::from(buf); let typed_array = TypedArray::from(buf);
// `send` can throw if the websocket isn't open (which can happen if it was closed by the // `send` can throw if the websocket isn't open (which can happen if it was closed by the
// remote). // remote).
let returned = js! { let returned = js! {
try { try {
@{&self.websocket}.send(@{typed_array.buffer()}); @{&self.websocket}.send(@{typed_array.buffer()});
return true; return true;
} catch(e) { } catch(e) {
return false; return false;
} }
}; };
match returned { match returned {
stdweb::Value::Bool(true) => Ok(buf.len()), stdweb::Value::Bool(true) => Ok(buf.len()),
stdweb::Value::Bool(false) => Err(IoError::new( stdweb::Value::Bool(false) => Err(IoError::new(
IoErrorKind::BrokenPipe, IoErrorKind::BrokenPipe,
"websocket has been closed by the remote", "websocket has been closed by the remote",
)), )),
_ => unreachable!(), _ => unreachable!(),
} }
} }
#[inline] #[inline]
fn flush(&mut self) -> Result<(), IoError> { fn flush(&mut self) -> Result<(), IoError> {
// Everything is always considered flushed. // Everything is always considered flushed.
Ok(()) Ok(())
} }
} }
// Tries to interpret the `Multiaddr` as a `/ipN/.../tcp/.../ws` multiaddress, and if so returns // Tries to interpret the `Multiaddr` as a `/ipN/.../tcp/.../ws` multiaddress, and if so returns
// the corresponding `ws://.../` URL. // the corresponding `ws://.../` URL.
fn multiaddr_to_target(addr: &Multiaddr) -> Result<String, ()> { fn multiaddr_to_target(addr: &Multiaddr) -> Result<String, ()> {
let protocols: Vec<_> = addr.iter().collect(); let protocols: Vec<_> = addr.iter().collect();
if protocols.len() != 3 { if protocols.len() != 3 {
return Err(()); return Err(());
} }
match (&protocols[0], &protocols[1], &protocols[2]) { match (&protocols[0], &protocols[1], &protocols[2]) {
(&AddrComponent::IP4(ref ip), &AddrComponent::TCP(port), &AddrComponent::WS) => { (&AddrComponent::IP4(ref ip), &AddrComponent::TCP(port), &AddrComponent::WS) => {
Ok(format!("ws://{}:{}/", ip, port)) Ok(format!("ws://{}:{}/", ip, port))
} }
(&AddrComponent::IP6(ref ip), &AddrComponent::TCP(port), &AddrComponent::WS) => { (&AddrComponent::IP6(ref ip), &AddrComponent::TCP(port), &AddrComponent::WS) => {
Ok(format!("ws://[{}]:{}/", ip, port)) Ok(format!("ws://[{}]:{}/", ip, port))
} }
(&AddrComponent::IP4(ref ip), &AddrComponent::TCP(port), &AddrComponent::WSS) => { (&AddrComponent::IP4(ref ip), &AddrComponent::TCP(port), &AddrComponent::WSS) => {
Ok(format!("wss://{}:{}/", ip, port)) Ok(format!("wss://{}:{}/", ip, port))
} }
(&AddrComponent::IP6(ref ip), &AddrComponent::TCP(port), &AddrComponent::WSS) => { (&AddrComponent::IP6(ref ip), &AddrComponent::TCP(port), &AddrComponent::WSS) => {
Ok(format!("wss://[{}]:{}/", ip, port)) Ok(format!("wss://[{}]:{}/", ip, port))
} }
_ => Err(()), _ => Err(()),
} }
} }
// TODO: write tests (tests are very difficult to write with emscripten) // TODO: write tests (tests are very difficult to write with emscripten)

View File

@ -37,268 +37,301 @@ use websocket::stream::async::Stream as AsyncStream;
/// > **Note**: `/wss` is only supported for dialing and not listening. /// > **Note**: `/wss` is only supported for dialing and not listening.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct WsConfig<T> { pub struct WsConfig<T> {
transport: T, transport: T,
} }
impl<T> WsConfig<T> { impl<T> WsConfig<T> {
/// Creates a new configuration object for websocket. /// Creates a new configuration object for websocket.
/// ///
/// The websockets will run on top of the `Transport` you pass as parameter. /// The websockets will run on top of the `Transport` you pass as parameter.
#[inline] #[inline]
pub fn new(inner: T) -> WsConfig<T> { pub fn new(inner: T) -> WsConfig<T> {
WsConfig { transport: inner } WsConfig { transport: inner }
} }
} }
impl<T> Transport for WsConfig<T> impl<T> Transport for WsConfig<T>
where where
T: Transport + 'static, // TODO: this 'static is pretty arbitrary and is necessary because of the websocket library // TODO: this 'static is pretty arbitrary and is necessary because of the websocket library
T::RawConn: Send, // TODO: this Send is pretty arbitrary and is necessary because of the websocket library T: Transport + 'static,
// TODO: this Send is pretty arbitrary and is necessary because of the websocket library
T::RawConn: Send,
{ {
type RawConn = Box<AsyncStream>; type RawConn = Box<AsyncStream>;
type Listener = stream::Map< type Listener =
T::Listener, stream::Map<T::Listener, fn(<T as Transport>::ListenerUpgrade) -> Self::ListenerUpgrade>;
fn(<T as Transport>::ListenerUpgrade) -> Self::ListenerUpgrade, type ListenerUpgrade = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>;
>; type Dial = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>;
type ListenerUpgrade = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>;
type Dial = Box<Future<Item = (Self::RawConn, Multiaddr), Error = IoError>>;
fn listen_on( fn listen_on(
self, self,
original_addr: Multiaddr, original_addr: Multiaddr,
) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> { ) -> Result<(Self::Listener, Multiaddr), (Self, Multiaddr)> {
let mut inner_addr = original_addr.clone(); let mut inner_addr = original_addr.clone();
match inner_addr.pop() { match inner_addr.pop() {
Some(AddrComponent::WS) => {} Some(AddrComponent::WS) => {}
_ => return Err((self, original_addr)), _ => return Err((self, original_addr)),
}; };
let (inner_listen, new_addr) = match self.transport.listen_on(inner_addr) { let (inner_listen, new_addr) = match self.transport.listen_on(inner_addr) {
Ok((listen, mut new_addr)) => { Ok((listen, mut new_addr)) => {
// Need to suffix `/ws` to the listening address. // Need to suffix `/ws` to the listening address.
new_addr.append(AddrComponent::WS); new_addr.append(AddrComponent::WS);
(listen, new_addr) (listen, new_addr)
} }
Err((transport, _)) => { Err((transport, _)) => {
return Err(( return Err((
WsConfig { WsConfig {
transport: transport, transport: transport,
}, },
original_addr, original_addr,
)); ));
} }
}; };
let listen = inner_listen.map::<_, fn(_) -> _>(|stream| { let listen = inner_listen.map::<_, fn(_) -> _>(|stream| {
// Upgrade the listener to websockets like the websockets library requires us to do. // Upgrade the listener to websockets like the websockets library requires us to do.
let upgraded = stream.and_then(|(stream, mut client_addr)| { let upgraded = stream.and_then(|(stream, mut client_addr)| {
// Need to suffix `/ws` to each client address. // Need to suffix `/ws` to each client address.
client_addr.append(AddrComponent::WS); client_addr.append(AddrComponent::WS);
stream stream
.into_ws() .into_ws()
.map_err(|e| IoError::new(IoErrorKind::Other, e.3)) .map_err(|e| IoError::new(IoErrorKind::Other, e.3))
.and_then(|stream| { .and_then(|stream| {
// Accept the next incoming connection. // Accept the next incoming connection.
stream stream
.accept() .accept()
.map_err(|err| IoError::new(IoErrorKind::Other, err)) .map_err(|err| IoError::new(IoErrorKind::Other, err))
.map(|(client, _http_headers)| { .map(|(client, _http_headers)| {
// Plug our own API on top of the `websockets` API. // Plug our own API on top of the `websockets` API.
let framed_data = client let framed_data = client
.map_err(|err| IoError::new(IoErrorKind::Other, err)) .map_err(|err| IoError::new(IoErrorKind::Other, err))
.sink_map_err(|err| IoError::new(IoErrorKind::Other, err)) .sink_map_err(|err| IoError::new(IoErrorKind::Other, err))
.with(|data| Ok(OwnedMessage::Binary(data))) .with(|data| Ok(OwnedMessage::Binary(data)))
.and_then(|recv| { .and_then(|recv| {
match recv { match recv {
OwnedMessage::Binary(data) => Ok(Some(data)), OwnedMessage::Binary(data) => Ok(Some(data)),
OwnedMessage::Text(data) => Ok(Some(data.into_bytes())), OwnedMessage::Text(data) => Ok(Some(data.into_bytes())),
OwnedMessage::Close(_) => Ok(None), OwnedMessage::Close(_) => Ok(None),
// TODO: handle pings and pongs, which is freaking hard // TODO: handle pings and pongs, which is freaking hard
// for now we close the socket when that happens // for now we close the socket when that happens
_ => Ok(None) _ => Ok(None)
} }
}) })
// TODO: is there a way to merge both lines into one? // TODO: is there a way to merge both lines into one?
.take_while(|v| Ok(v.is_some())) .take_while(|v| Ok(v.is_some()))
.map(|v| v.expect("we only take while this is Some")); .map(|v| v.expect("we only take while this is Some"));
let read_write = RwStreamSink::new(framed_data); let read_write = RwStreamSink::new(framed_data);
Box::new(read_write) as Box<AsyncStream> Box::new(read_write) as Box<AsyncStream>
}) })
}) })
.map(|s| Box::new(Ok(s).into_future()) as Box<Future<Item = _, Error = _>>) .map(|s| Box::new(Ok(s).into_future()) as Box<Future<Item = _, Error = _>>)
.into_future() .into_future()
.flatten() .flatten()
.map(move |v| (v, client_addr)) .map(move |v| (v, client_addr))
}); });
Box::new(upgraded) as Box<Future<Item = _, Error = _>> Box::new(upgraded) as Box<Future<Item = _, Error = _>>
}); });
Ok((listen, new_addr)) Ok((listen, new_addr))
} }
fn dial(self, original_addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> { fn dial(self, original_addr: Multiaddr) -> Result<Self::Dial, (Self, Multiaddr)> {
let mut inner_addr = original_addr.clone(); let mut inner_addr = original_addr.clone();
let is_wss = match inner_addr.pop() { let is_wss = match inner_addr.pop() {
Some(AddrComponent::WS) => false, Some(AddrComponent::WS) => false,
Some(AddrComponent::WSS) => true, Some(AddrComponent::WSS) => true,
_ => return Err((self, original_addr)), _ => return Err((self, original_addr)),
}; };
let inner_dial = match self.transport.dial(inner_addr) { let inner_dial = match self.transport.dial(inner_addr) {
Ok(d) => d, Ok(d) => d,
Err((transport, _)) => { Err((transport, _)) => {
return Err(( return Err((
WsConfig { WsConfig {
transport: transport, transport: transport,
}, },
original_addr, original_addr,
)); ));
} }
}; };
let dial = inner_dial.into_future().and_then(move |(connec, client_addr)| { let dial = inner_dial
// We pass a dummy address to `ClientBuilder` because it is never used anywhere .into_future()
// in the negotiation anyway, and we use `async_connect_on` to pass a stream. .and_then(move |(connec, client_addr)| {
ClientBuilder::new(if is_wss { "wss://127.0.0.1" } else { "ws://127.0.0.1" }) // We pass a dummy address to `ClientBuilder` because it is never used anywhere
.expect("hard-coded ws address is always valid") // in the negotiation anyway, and we use `async_connect_on` to pass a stream.
.async_connect_on(connec) ClientBuilder::new(if is_wss {
.map_err(|err| IoError::new(IoErrorKind::Other, err)) "wss://127.0.0.1"
.map(|(client, _)| { } else {
// Plug our own API on top of the API of the websockets library. "ws://127.0.0.1"
let framed_data = client }).expect("hard-coded ws address is always valid")
.map_err(|err| IoError::new(IoErrorKind::Other, err)) .async_connect_on(connec)
.sink_map_err(|err| IoError::new(IoErrorKind::Other, err)) .map_err(|err| IoError::new(IoErrorKind::Other, err))
.with(|data| Ok(OwnedMessage::Binary(data))) .map(|(client, _)| {
.and_then(|recv| { // Plug our own API on top of the API of the websockets library.
match recv { let framed_data = client
OwnedMessage::Binary(data) => Ok(data), .map_err(|err| IoError::new(IoErrorKind::Other, err))
OwnedMessage::Text(data) => Ok(data.into_bytes()), .sink_map_err(|err| IoError::new(IoErrorKind::Other, err))
// TODO: pings and pongs and close messages need to be .with(|data| Ok(OwnedMessage::Binary(data)))
// answered ; and this is really hard ; for now we produce .and_then(|recv| {
// an error when that happens match recv {
_ => Err(IoError::new(IoErrorKind::Other, "unimplemented")), OwnedMessage::Binary(data) => Ok(data),
} OwnedMessage::Text(data) => Ok(data.into_bytes()),
}); // TODO: pings and pongs and close messages need to be
let read_write = RwStreamSink::new(framed_data); // answered ; and this is really hard ; for now we produce
Box::new(read_write) as Box<AsyncStream> // an error when that happens
}) _ => Err(IoError::new(IoErrorKind::Other, "unimplemented")),
.map(|c| (c, client_addr)) }
}); });
let read_write = RwStreamSink::new(framed_data);
Box::new(read_write) as Box<AsyncStream>
})
.map(|c| (c, client_addr))
});
Ok(Box::new(dial) as Box<_>) Ok(Box::new(dial) as Box<_>)
} }
fn nat_traversal(&self, server: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> { fn nat_traversal(&self, server: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> {
let mut server = server.clone(); let mut server = server.clone();
let last_proto = match server.pop() { let last_proto = match server.pop() {
Some(v @ AddrComponent::WS) | Some(v @ AddrComponent::WSS) => v, Some(v @ AddrComponent::WS) | Some(v @ AddrComponent::WSS) => v,
_ => return None, _ => return None,
}; };
let mut observed = observed.clone(); let mut observed = observed.clone();
match observed.pop() { match observed.pop() {
Some(AddrComponent::WS) => false, Some(AddrComponent::WS) => false,
Some(AddrComponent::WSS) => true, Some(AddrComponent::WSS) => true,
_ => return None, _ => return None,
}; };
self.transport.nat_traversal(&server, &observed) self.transport
.map(move |mut result| { result.append(last_proto); result }) .nat_traversal(&server, &observed)
} .map(move |mut result| {
result.append(last_proto);
result
})
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate libp2p_tcp_transport as tcp; extern crate libp2p_tcp_transport as tcp;
extern crate tokio_core; extern crate tokio_core;
use self::tokio_core::reactor::Core; use self::tokio_core::reactor::Core;
use WsConfig; use WsConfig;
use futures::{Future, Stream}; use futures::{Future, Stream};
use multiaddr::Multiaddr; use multiaddr::Multiaddr;
use swarm::Transport; use swarm::Transport;
#[test] #[test]
fn dialer_connects_to_listener_ipv4() { fn dialer_connects_to_listener_ipv4() {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let ws_config = WsConfig::new(tcp::TcpConfig::new(core.handle())); let ws_config = WsConfig::new(tcp::TcpConfig::new(core.handle()));
let (listener, addr) = ws_config let (listener, addr) = ws_config
.clone() .clone()
.listen_on("/ip4/0.0.0.0/tcp/0/ws".parse().unwrap()) .listen_on("/ip4/0.0.0.0/tcp/0/ws".parse().unwrap())
.unwrap(); .unwrap();
assert!(addr.to_string().ends_with("/ws")); assert!(addr.to_string().ends_with("/ws"));
assert!(!addr.to_string().ends_with("/0/ws")); assert!(!addr.to_string().ends_with("/0/ws"));
let listener = listener let listener = listener
.into_future() .into_future()
.map_err(|(e, _)| e) .map_err(|(e, _)| e)
.and_then(|(c, _)| c.unwrap().map(|v| v.0)); .and_then(|(c, _)| c.unwrap().map(|v| v.0));
let dialer = ws_config.clone().dial(addr).unwrap().map(|v| v.0); let dialer = ws_config.clone().dial(addr).unwrap().map(|v| v.0);
let future = listener let future = listener
.select(dialer) .select(dialer)
.map_err(|(e, _)| e) .map_err(|(e, _)| e)
.and_then(|(_, n)| n); .and_then(|(_, n)| n);
core.run(future).unwrap(); core.run(future).unwrap();
} }
#[test] #[test]
fn dialer_connects_to_listener_ipv6() { fn dialer_connects_to_listener_ipv6() {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let ws_config = WsConfig::new(tcp::TcpConfig::new(core.handle())); let ws_config = WsConfig::new(tcp::TcpConfig::new(core.handle()));
let (listener, addr) = ws_config let (listener, addr) = ws_config
.clone() .clone()
.listen_on("/ip6/::1/tcp/0/ws".parse().unwrap()) .listen_on("/ip6/::1/tcp/0/ws".parse().unwrap())
.unwrap(); .unwrap();
assert!(addr.to_string().ends_with("/ws")); assert!(addr.to_string().ends_with("/ws"));
assert!(!addr.to_string().ends_with("/0/ws")); assert!(!addr.to_string().ends_with("/0/ws"));
let listener = listener let listener = listener
.into_future() .into_future()
.map_err(|(e, _)| e) .map_err(|(e, _)| e)
.and_then(|(c, _)| c.unwrap().map(|v| v.0)); .and_then(|(c, _)| c.unwrap().map(|v| v.0));
let dialer = ws_config.clone().dial(addr).unwrap().map(|v| v.0); let dialer = ws_config.clone().dial(addr).unwrap().map(|v| v.0);
let future = listener let future = listener
.select(dialer) .select(dialer)
.map_err(|(e, _)| e) .map_err(|(e, _)| e)
.and_then(|(_, n)| n); .and_then(|(_, n)| n);
core.run(future).unwrap(); core.run(future).unwrap();
} }
#[test] #[test]
fn nat_traversal() { fn nat_traversal() {
let core = Core::new().unwrap(); let core = Core::new().unwrap();
let ws_config = WsConfig::new(tcp::TcpConfig::new(core.handle())); let ws_config = WsConfig::new(tcp::TcpConfig::new(core.handle()));
{ {
let server = "/ip4/127.0.0.1/tcp/10000/ws".parse::<Multiaddr>().unwrap(); let server = "/ip4/127.0.0.1/tcp/10000/ws".parse::<Multiaddr>().unwrap();
let observed = "/ip4/80.81.82.83/tcp/25000/ws".parse::<Multiaddr>().unwrap(); let observed = "/ip4/80.81.82.83/tcp/25000/ws"
assert_eq!(ws_config.nat_traversal(&server, &observed).unwrap(), .parse::<Multiaddr>()
"/ip4/80.81.82.83/tcp/10000/ws".parse::<Multiaddr>().unwrap()); .unwrap();
} assert_eq!(
ws_config.nat_traversal(&server, &observed).unwrap(),
"/ip4/80.81.82.83/tcp/10000/ws"
.parse::<Multiaddr>()
.unwrap()
);
}
{ {
let server = "/ip4/127.0.0.1/tcp/10000/wss".parse::<Multiaddr>().unwrap(); let server = "/ip4/127.0.0.1/tcp/10000/wss".parse::<Multiaddr>().unwrap();
let observed = "/ip4/80.81.82.83/tcp/25000/wss".parse::<Multiaddr>().unwrap(); let observed = "/ip4/80.81.82.83/tcp/25000/wss"
assert_eq!(ws_config.nat_traversal(&server, &observed).unwrap(), .parse::<Multiaddr>()
"/ip4/80.81.82.83/tcp/10000/wss".parse::<Multiaddr>().unwrap()); .unwrap();
} assert_eq!(
ws_config.nat_traversal(&server, &observed).unwrap(),
"/ip4/80.81.82.83/tcp/10000/wss"
.parse::<Multiaddr>()
.unwrap()
);
}
{ {
let server = "/ip4/127.0.0.1/tcp/10000/ws".parse::<Multiaddr>().unwrap(); let server = "/ip4/127.0.0.1/tcp/10000/ws".parse::<Multiaddr>().unwrap();
let observed = "/ip4/80.81.82.83/tcp/25000/wss".parse::<Multiaddr>().unwrap(); let observed = "/ip4/80.81.82.83/tcp/25000/wss"
assert_eq!(ws_config.nat_traversal(&server, &observed).unwrap(), .parse::<Multiaddr>()
"/ip4/80.81.82.83/tcp/10000/ws".parse::<Multiaddr>().unwrap()); .unwrap();
} assert_eq!(
ws_config.nat_traversal(&server, &observed).unwrap(),
"/ip4/80.81.82.83/tcp/10000/ws"
.parse::<Multiaddr>()
.unwrap()
);
}
{ {
let server = "/ip4/127.0.0.1/tcp/10000/wss".parse::<Multiaddr>().unwrap(); let server = "/ip4/127.0.0.1/tcp/10000/wss".parse::<Multiaddr>().unwrap();
let observed = "/ip4/80.81.82.83/tcp/25000/ws".parse::<Multiaddr>().unwrap(); let observed = "/ip4/80.81.82.83/tcp/25000/ws"
assert_eq!(ws_config.nat_traversal(&server, &observed).unwrap(), .parse::<Multiaddr>()
"/ip4/80.81.82.83/tcp/10000/wss".parse::<Multiaddr>().unwrap()); .unwrap();
} assert_eq!(
} ws_config.nat_traversal(&server, &observed).unwrap(),
"/ip4/80.81.82.83/tcp/10000/wss"
.parse::<Multiaddr>()
.unwrap()
);
}
}
} }

View File

@ -381,7 +381,7 @@ mod tests {
assert_eq!( assert_eq!(
substream substream
.name() .name()
.and_then(|bytes| { String::from_utf8(bytes.to_vec()).ok() }), .and_then(|bytes| String::from_utf8(bytes.to_vec()).ok()),
Some(id.to_string()) Some(id.to_string())
); );
@ -395,7 +395,7 @@ mod tests {
assert_eq!( assert_eq!(
substream substream
.name() .name()
.and_then(|bytes| { String::from_utf8(bytes.to_vec()).ok() }), .and_then(|bytes| String::from_utf8(bytes.to_vec()).ok()),
Some(id.to_string()) Some(id.to_string())
); );
@ -445,7 +445,7 @@ mod tests {
assert_eq!( assert_eq!(
substream substream
.name() .name()
.and_then(|bytes| { String::from_utf8(bytes.to_vec()).ok() }), .and_then(|bytes| String::from_utf8(bytes.to_vec()).ok()),
Some(id.to_string()) Some(id.to_string())
); );

View File

@ -27,10 +27,10 @@ extern crate tokio_core;
extern crate tokio_io; extern crate tokio_io;
use futures::future::Future; use futures::future::Future;
use futures::{Stream, Sink}; use futures::{Sink, Stream};
use std::sync::mpsc; use std::sync::mpsc;
use std::thread; use std::thread;
use swarm::{Transport, StreamMuxer}; use swarm::{StreamMuxer, Transport};
use tcp::TcpConfig; use tcp::TcpConfig;
use tokio_core::reactor::Core; use tokio_core::reactor::Core;
use tokio_io::codec::length_delimited::Framed; use tokio_io::codec::length_delimited::Framed;
@ -43,11 +43,11 @@ fn client_to_server_outbound() {
let bg_thread = thread::spawn(move || { let bg_thread = thread::spawn(move || {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let transport = TcpConfig::new(core.handle()) let transport = TcpConfig::new(core.handle()).with_upgrade(multiplex::MultiplexConfig);
.with_upgrade(multiplex::MultiplexConfig);
let (listener, addr) = transport let (listener, addr) = transport
.listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap()).unwrap(); .listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap())
.unwrap();
tx.send(addr).unwrap(); tx.send(addr).unwrap();
let future = listener let future = listener
@ -57,7 +57,8 @@ fn client_to_server_outbound() {
.and_then(|client| client.outbound()) .and_then(|client| client.outbound())
.map(|client| Framed::<_, bytes::BytesMut>::new(client)) .map(|client| Framed::<_, bytes::BytesMut>::new(client))
.and_then(|client| { .and_then(|client| {
client.into_future() client
.into_future()
.map_err(|(err, _)| err) .map_err(|(err, _)| err)
.map(|(msg, _)| msg) .map(|(msg, _)| msg)
}) })
@ -71,10 +72,11 @@ fn client_to_server_outbound() {
}); });
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let transport = TcpConfig::new(core.handle()) let transport = TcpConfig::new(core.handle()).with_upgrade(multiplex::MultiplexConfig);
.with_upgrade(multiplex::MultiplexConfig);
let future = transport.dial(rx.recv().unwrap()).unwrap() let future = transport
.dial(rx.recv().unwrap())
.unwrap()
.and_then(|client| client.0.inbound()) .and_then(|client| client.0.inbound())
.map(|server| Framed::<_, bytes::BytesMut>::new(server)) .map(|server| Framed::<_, bytes::BytesMut>::new(server))
.and_then(|server| server.send("hello world".into())) .and_then(|server| server.send("hello world".into()))
@ -92,11 +94,11 @@ fn client_to_server_inbound() {
let bg_thread = thread::spawn(move || { let bg_thread = thread::spawn(move || {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let transport = TcpConfig::new(core.handle()) let transport = TcpConfig::new(core.handle()).with_upgrade(multiplex::MultiplexConfig);
.with_upgrade(multiplex::MultiplexConfig);
let (listener, addr) = transport let (listener, addr) = transport
.listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap()).unwrap(); .listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap())
.unwrap();
tx.send(addr).unwrap(); tx.send(addr).unwrap();
let future = listener let future = listener
@ -106,7 +108,8 @@ fn client_to_server_inbound() {
.and_then(|client| client.inbound()) .and_then(|client| client.inbound())
.map(|client| Framed::<_, bytes::BytesMut>::new(client)) .map(|client| Framed::<_, bytes::BytesMut>::new(client))
.and_then(|client| { .and_then(|client| {
client.into_future() client
.into_future()
.map_err(|(err, _)| err) .map_err(|(err, _)| err)
.map(|(msg, _)| msg) .map(|(msg, _)| msg)
}) })
@ -120,10 +123,11 @@ fn client_to_server_inbound() {
}); });
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let transport = TcpConfig::new(core.handle()) let transport = TcpConfig::new(core.handle()).with_upgrade(multiplex::MultiplexConfig);
.with_upgrade(multiplex::MultiplexConfig);
let future = transport.dial(rx.recv().unwrap()).unwrap() let future = transport
.dial(rx.recv().unwrap())
.unwrap()
.and_then(|(client, _)| client.outbound()) .and_then(|(client, _)| client.outbound())
.map(|server| Framed::<_, bytes::BytesMut>::new(server)) .map(|server| Framed::<_, bytes::BytesMut>::new(server))
.and_then(|server| server.send("hello world".into())) .and_then(|server| server.send("hello world".into()))

View File

@ -1,21 +1,21 @@
// Copyright 2017 Parity Technologies (UK) Ltd. // Copyright 2017 Parity Technologies (UK) Ltd.
// //
// Permission is hereby granted, free of charge, to any person obtaining a // Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"), // copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation // to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, // the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the // and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions: // Software is furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
//! Contains the `dialer_select_proto` code, which allows selecting a protocol thanks to //! Contains the `dialer_select_proto` code, which allows selecting a protocol thanks to
@ -24,7 +24,7 @@
use ProtocolChoiceError; use ProtocolChoiceError;
use bytes::Bytes; use bytes::Bytes;
use futures::{Future, Sink, Stream}; use futures::{Future, Sink, Stream};
use futures::future::{result, loop_fn, Loop}; use futures::future::{loop_fn, result, Loop};
use protocol::Dialer; use protocol::Dialer;
use protocol::DialerToListenerMessage; use protocol::DialerToListenerMessage;
@ -45,20 +45,21 @@ use tokio_io::{AsyncRead, AsyncWrite};
// TODO: remove the Box once -> impl Trait lands // TODO: remove the Box once -> impl Trait lands
#[inline] #[inline]
pub fn dialer_select_proto<'a, R, I, M, P>( pub fn dialer_select_proto<'a, R, I, M, P>(
inner: R, inner: R,
protocols: I, protocols: I,
) -> Box<Future<Item = (P, R), Error = ProtocolChoiceError> + 'a> ) -> Box<Future<Item = (P, R), Error = ProtocolChoiceError> + 'a>
where R: AsyncRead + AsyncWrite + 'a, where
I: Iterator<Item = (Bytes, M, P)> + 'a, R: AsyncRead + AsyncWrite + 'a,
M: FnMut(&Bytes, &Bytes) -> bool + 'a, I: Iterator<Item = (Bytes, M, P)> + 'a,
P: 'a M: FnMut(&Bytes, &Bytes) -> bool + 'a,
P: 'a,
{ {
// We choose between the "serial" and "parallel" strategies based on the number of protocols. // We choose between the "serial" and "parallel" strategies based on the number of protocols.
if protocols.size_hint().1.map(|n| n <= 3).unwrap_or(false) { if protocols.size_hint().1.map(|n| n <= 3).unwrap_or(false) {
dialer_select_proto_serial(inner, protocols.map(|(n, _, id)| (n, id))) dialer_select_proto_serial(inner, protocols.map(|(n, _, id)| (n, id)))
} else { } else {
dialer_select_proto_parallel(inner, protocols) dialer_select_proto_parallel(inner, protocols)
} }
} }
/// Helps selecting a protocol amongst the ones supported. /// Helps selecting a protocol amongst the ones supported.
@ -67,22 +68,24 @@ pub fn dialer_select_proto<'a, R, I, M, P>(
/// match functions, because it's not needed. /// match functions, because it's not needed.
// TODO: remove the Box once -> impl Trait lands // TODO: remove the Box once -> impl Trait lands
pub fn dialer_select_proto_serial<'a, R, I, P>( pub fn dialer_select_proto_serial<'a, R, I, P>(
inner: R, inner: R,
mut protocols: I, mut protocols: I,
) -> Box<Future<Item = (P, R), Error = ProtocolChoiceError> + 'a> ) -> Box<Future<Item = (P, R), Error = ProtocolChoiceError> + 'a>
where R: AsyncRead + AsyncWrite + 'a, where
I: Iterator<Item = (Bytes, P)> + 'a, R: AsyncRead + AsyncWrite + 'a,
P: 'a I: Iterator<Item = (Bytes, P)> + 'a,
P: 'a,
{ {
let future = Dialer::new(inner) let future = Dialer::new(inner).from_err().and_then(move |dialer| {
.from_err() // Similar to a `loop` keyword.
.and_then(move |dialer| { loop_fn(dialer, move |dialer| {
// Similar to a `loop` keyword. result(protocols.next().ok_or(ProtocolChoiceError::NoProtocolFound))
loop_fn(dialer, move |dialer| {
result(protocols.next().ok_or(ProtocolChoiceError::NoProtocolFound))
// If the `protocols` iterator produced an element, send it to the dialer // If the `protocols` iterator produced an element, send it to the dialer
.and_then(|(proto_name, proto_value)| { .and_then(|(proto_name, proto_value)| {
dialer.send(DialerToListenerMessage::ProtocolRequest { name: proto_name.clone() }) let req = DialerToListenerMessage::ProtocolRequest {
name: proto_name.clone()
};
dialer.send(req)
.map(|d| (d, proto_name, proto_value)) .map(|d| (d, proto_name, proto_value))
.from_err() .from_err()
}) })
@ -110,11 +113,11 @@ pub fn dialer_select_proto_serial<'a, R, I, P>(
_ => Err(ProtocolChoiceError::UnexpectedMessage), _ => Err(ProtocolChoiceError::UnexpectedMessage),
} }
}) })
}) })
}); });
// The "Rust doesn't have impl Trait yet" tax. // The "Rust doesn't have impl Trait yet" tax.
Box::new(future) Box::new(future)
} }
/// Helps selecting a protocol amongst the ones supported. /// Helps selecting a protocol amongst the ones supported.
@ -123,60 +126,67 @@ pub fn dialer_select_proto_serial<'a, R, I, P>(
/// chooses the most appropriate one. /// chooses the most appropriate one.
// TODO: remove the Box once -> impl Trait lands // TODO: remove the Box once -> impl Trait lands
pub fn dialer_select_proto_parallel<'a, R, I, M, P>( pub fn dialer_select_proto_parallel<'a, R, I, M, P>(
inner: R, inner: R,
protocols: I, protocols: I,
) -> Box<Future<Item = (P, R), Error = ProtocolChoiceError> + 'a> ) -> Box<Future<Item = (P, R), Error = ProtocolChoiceError> + 'a>
where R: AsyncRead + AsyncWrite + 'a, where
I: Iterator<Item = (Bytes, M, P)> + 'a, R: AsyncRead + AsyncWrite + 'a,
M: FnMut(&Bytes, &Bytes) -> bool + 'a, I: Iterator<Item = (Bytes, M, P)> + 'a,
P: 'a M: FnMut(&Bytes, &Bytes) -> bool + 'a,
P: 'a,
{ {
let future = Dialer::new(inner) let future = Dialer::new(inner)
.from_err() .from_err()
.and_then( .and_then(move |dialer| {
move |dialer| dialer.send(DialerToListenerMessage::ProtocolsListRequest).from_err(), dialer
) .send(DialerToListenerMessage::ProtocolsListRequest)
.and_then(move |dialer| dialer.into_future().map_err(|(e, _)| e.into())) .from_err()
.and_then(move |(msg, dialer)| { })
let list = match msg { .and_then(move |dialer| dialer.into_future().map_err(|(e, _)| e.into()))
Some(ListenerToDialerMessage::ProtocolsListResponse { list }) => list, .and_then(move |(msg, dialer)| {
_ => return Err(ProtocolChoiceError::UnexpectedMessage), let list = match msg {
}; Some(ListenerToDialerMessage::ProtocolsListResponse { list }) => list,
_ => return Err(ProtocolChoiceError::UnexpectedMessage),
};
let mut found = None; let mut found = None;
for (local_name, mut match_fn, ident) in protocols { for (local_name, mut match_fn, ident) in protocols {
for remote_name in &list { for remote_name in &list {
if match_fn(remote_name, &local_name) { if match_fn(remote_name, &local_name) {
found = Some((remote_name.clone(), ident)); found = Some((remote_name.clone(), ident));
break; break;
} }
} }
if found.is_some() { if found.is_some() {
break; break;
} }
} }
let (proto_name, proto_val) = found.ok_or(ProtocolChoiceError::NoProtocolFound)?; let (proto_name, proto_val) = found.ok_or(ProtocolChoiceError::NoProtocolFound)?;
Ok((proto_name, proto_val, dialer)) Ok((proto_name, proto_val, dialer))
}) })
.and_then(|(proto_name, proto_val, dialer)| { .and_then(|(proto_name, proto_val, dialer)| {
dialer.send(DialerToListenerMessage::ProtocolRequest { name: proto_name.clone() }) dialer
.from_err() .send(DialerToListenerMessage::ProtocolRequest {
.map(|dialer| (proto_name, proto_val, dialer)) name: proto_name.clone(),
}) })
.and_then(|(proto_name, proto_val, dialer)| { .from_err()
dialer.into_future() .map(|dialer| (proto_name, proto_val, dialer))
.map(|(msg, rest)| (proto_name, proto_val, msg, rest)) })
.map_err(|(err, _)| err.into()) .and_then(|(proto_name, proto_val, dialer)| {
}) dialer
.and_then(|(proto_name, proto_val, msg, dialer)| match msg { .into_future()
Some(ListenerToDialerMessage::ProtocolAck { ref name }) if name == &proto_name => { .map(|(msg, rest)| (proto_name, proto_val, msg, rest))
Ok((proto_val, dialer.into_inner())) .map_err(|(err, _)| err.into())
} })
_ => Err(ProtocolChoiceError::UnexpectedMessage), .and_then(|(proto_name, proto_val, msg, dialer)| match msg {
}); Some(ListenerToDialerMessage::ProtocolAck { ref name }) if name == &proto_name => {
Ok((proto_val, dialer.into_inner()))
}
_ => Err(ProtocolChoiceError::UnexpectedMessage),
});
// The "Rust doesn't have impl Trait yet" tax. // The "Rust doesn't have impl Trait yet" tax.
Box::new(future) Box::new(future)
} }

View File

@ -1,21 +1,21 @@
// Copyright 2017 Parity Technologies (UK) Ltd. // Copyright 2017 Parity Technologies (UK) Ltd.
// //
// Permission is hereby granted, free of charge, to any person obtaining a // Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"), // copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation // to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, // the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the // and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions: // Software is furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
//! Main `ProtocolChoiceError` error. //! Main `ProtocolChoiceError` error.
@ -28,59 +28,55 @@ use std::io::Error as IoError;
/// Error that can happen when negotiating a protocol with the remote. /// Error that can happen when negotiating a protocol with the remote.
#[derive(Debug)] #[derive(Debug)]
pub enum ProtocolChoiceError { pub enum ProtocolChoiceError {
/// Error in the protocol. /// Error in the protocol.
MultistreamSelectError(MultistreamSelectError), MultistreamSelectError(MultistreamSelectError),
/// Received a message from the remote that makes no sense in the current context. /// Received a message from the remote that makes no sense in the current context.
UnexpectedMessage, UnexpectedMessage,
/// We don't support any protocol in common with the remote. /// We don't support any protocol in common with the remote.
NoProtocolFound, NoProtocolFound,
} }
impl From<MultistreamSelectError> for ProtocolChoiceError { impl From<MultistreamSelectError> for ProtocolChoiceError {
#[inline] #[inline]
fn from(err: MultistreamSelectError) -> ProtocolChoiceError { fn from(err: MultistreamSelectError) -> ProtocolChoiceError {
ProtocolChoiceError::MultistreamSelectError(err) ProtocolChoiceError::MultistreamSelectError(err)
} }
} }
impl From<IoError> for ProtocolChoiceError { impl From<IoError> for ProtocolChoiceError {
#[inline] #[inline]
fn from(err: IoError) -> ProtocolChoiceError { fn from(err: IoError) -> ProtocolChoiceError {
MultistreamSelectError::from(err).into() MultistreamSelectError::from(err).into()
} }
} }
impl error::Error for ProtocolChoiceError { impl error::Error for ProtocolChoiceError {
#[inline] #[inline]
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
ProtocolChoiceError::MultistreamSelectError(_) => { ProtocolChoiceError::MultistreamSelectError(_) => "error in the protocol",
"error in the protocol" ProtocolChoiceError::UnexpectedMessage => {
}, "received a message from the remote that makes no sense in the current context"
ProtocolChoiceError::UnexpectedMessage => { }
"received a message from the remote that makes no sense in the current context" ProtocolChoiceError::NoProtocolFound => {
}, "we don't support any protocol in common with the remote"
ProtocolChoiceError::NoProtocolFound => { }
"we don't support any protocol in common with the remote" }
}, }
}
}
fn cause(&self) -> Option<&error::Error> { fn cause(&self) -> Option<&error::Error> {
match *self { match *self {
ProtocolChoiceError::MultistreamSelectError(ref err) => { ProtocolChoiceError::MultistreamSelectError(ref err) => Some(err),
Some(err) _ => None,
} }
_ => None, }
}
}
} }
impl fmt::Display for ProtocolChoiceError { impl fmt::Display for ProtocolChoiceError {
#[inline] #[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self)) write!(fmt, "{}", error::Error::description(self))
} }
} }

View File

@ -1,34 +1,34 @@
// Copyright 2017 Parity Technologies (UK) Ltd. // Copyright 2017 Parity Technologies (UK) Ltd.
// //
// Permission is hereby granted, free of charge, to any person obtaining a // Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"), // copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation // to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, // the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the // and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions: // Software is furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
//! Alternative implementation for `tokio_io::codec::length_delimited::FramedRead` with an //! Alternative implementation for `tokio_io::codec::length_delimited::FramedRead` with an
//! additional property: the `into_inner()` method is guarateed not to drop any data. //! additional property: the `into_inner()` method is guarateed not to drop any data.
//! //!
//! Also has the length field length hardcoded. //! Also has the length field length hardcoded.
//! //!
//! We purposely only support a frame length of under 64kiB. Frames most consist in a short //! We purposely only support a frame length of under 64kiB. Frames most consist in a short
//! protocol name, which is highly unlikely to be more than 64kiB long. //! protocol name, which is highly unlikely to be more than 64kiB long.
use std::io::{Error as IoError, ErrorKind as IoErrorKind}; use std::io::{Error as IoError, ErrorKind as IoErrorKind};
use std::marker::PhantomData; use std::marker::PhantomData;
use futures::{Async, StartSend, Poll, Sink, Stream}; use futures::{Async, Poll, Sink, StartSend, Stream};
use smallvec::SmallVec; use smallvec::SmallVec;
use tokio_io::AsyncRead; use tokio_io::AsyncRead;
@ -56,16 +56,18 @@ enum State {
// We are currently reading the length of the next frame of data. // We are currently reading the length of the next frame of data.
ReadingLength, ReadingLength,
// We are currently reading the frame of data itself. // We are currently reading the frame of data itself.
ReadingData { ReadingData { frame_len: u16 },
frame_len: u16
},
} }
impl<I, S> LengthDelimitedFramedRead<I, S> { impl<I, S> LengthDelimitedFramedRead<I, S> {
pub fn new(inner: S) -> LengthDelimitedFramedRead<I, S> { pub fn new(inner: S) -> LengthDelimitedFramedRead<I, S> {
LengthDelimitedFramedRead { LengthDelimitedFramedRead {
inner: inner, inner: inner,
internal_buffer: { let mut v = SmallVec::new(); v.push(0); v }, internal_buffer: {
let mut v = SmallVec::new();
v.push(0);
v
},
internal_buffer_pos: 0, internal_buffer_pos: 0,
state: State::ReadingLength, state: State::ReadingLength,
marker: PhantomData, marker: PhantomData,
@ -92,8 +94,9 @@ impl<I, S> LengthDelimitedFramedRead<I, S> {
} }
impl<I, S> Stream for LengthDelimitedFramedRead<I, S> impl<I, S> Stream for LengthDelimitedFramedRead<I, S>
where S: AsyncRead, where
I: for<'r> From<&'r [u8]> S: AsyncRead,
I: for<'r> From<&'r [u8]>,
{ {
type Item = I; type Item = I;
type Error = IoError; type Error = IoError;
@ -105,26 +108,27 @@ impl<I, S> Stream for LengthDelimitedFramedRead<I, S>
match self.state { match self.state {
State::ReadingLength => { State::ReadingLength => {
match self.inner.read(&mut self.internal_buffer[self.internal_buffer_pos..]) { match self.inner
.read(&mut self.internal_buffer[self.internal_buffer_pos..])
{
Ok(0) => { Ok(0) => {
// EOF // EOF
if self.internal_buffer_pos == 0 { if self.internal_buffer_pos == 0 {
return Ok(Async::Ready(None)); return Ok(Async::Ready(None));
} else { } else {
return Err(IoError::new(IoErrorKind::BrokenPipe, return Err(IoError::new(IoErrorKind::BrokenPipe, "unexpected eof"));
"unexpected eof"));
} }
}, }
Ok(n) => { Ok(n) => {
debug_assert_eq!(n, 1); debug_assert_eq!(n, 1);
self.internal_buffer_pos += n; self.internal_buffer_pos += n;
}, }
Err(ref err) if err.kind() == IoErrorKind::WouldBlock => { Err(ref err) if err.kind() == IoErrorKind::WouldBlock => {
return Ok(Async::NotReady); return Ok(Async::NotReady);
}, }
Err(err) => { Err(err) => {
return Err(err); return Err(err);
}, }
}; };
debug_assert_eq!(self.internal_buffer.len(), self.internal_buffer_pos); debug_assert_eq!(self.internal_buffer.len(), self.internal_buffer_pos);
@ -135,12 +139,13 @@ impl<I, S> Stream for LengthDelimitedFramedRead<I, S>
let frame_len = decode_length_prefix(&self.internal_buffer); let frame_len = decode_length_prefix(&self.internal_buffer);
if frame_len >= 1 { if frame_len >= 1 {
self.state = State::ReadingData { frame_len: frame_len }; self.state = State::ReadingData {
frame_len: frame_len,
};
self.internal_buffer.clear(); self.internal_buffer.clear();
self.internal_buffer.reserve(frame_len as usize); self.internal_buffer.reserve(frame_len as usize);
self.internal_buffer.extend((0 .. frame_len).map(|_| 0)); self.internal_buffer.extend((0..frame_len).map(|_| 0));
self.internal_buffer_pos = 0; self.internal_buffer_pos = 0;
} else { } else {
debug_assert_eq!(frame_len, 0); debug_assert_eq!(frame_len, 0);
self.state = State::ReadingLength; self.state = State::ReadingLength;
@ -149,29 +154,32 @@ impl<I, S> Stream for LengthDelimitedFramedRead<I, S>
self.internal_buffer_pos = 0; self.internal_buffer_pos = 0;
return Ok(Async::Ready(Some(From::from(&[][..])))); return Ok(Async::Ready(Some(From::from(&[][..]))));
} }
} else if self.internal_buffer_pos >= 2 { } else if self.internal_buffer_pos >= 2 {
// Length prefix is too long. See module doc for info about max frame len. // Length prefix is too long. See module doc for info about max frame len.
return Err(IoError::new(IoErrorKind::InvalidData, "frame length too long")); return Err(IoError::new(
IoErrorKind::InvalidData,
"frame length too long",
));
} else { } else {
// Prepare for next read. // Prepare for next read.
self.internal_buffer.push(0); self.internal_buffer.push(0);
} }
}, }
State::ReadingData { frame_len } => { State::ReadingData { frame_len } => {
match self.inner.read(&mut self.internal_buffer[self.internal_buffer_pos..]) { match self.inner
.read(&mut self.internal_buffer[self.internal_buffer_pos..])
{
Ok(0) => { Ok(0) => {
return Err(IoError::new(IoErrorKind::BrokenPipe, "unexpected eof")); return Err(IoError::new(IoErrorKind::BrokenPipe, "unexpected eof"));
}, }
Ok(n) => self.internal_buffer_pos += n, Ok(n) => self.internal_buffer_pos += n,
Err(ref err) if err.kind() == IoErrorKind::WouldBlock => { Err(ref err) if err.kind() == IoErrorKind::WouldBlock => {
return Ok(Async::NotReady); return Ok(Async::NotReady);
}, }
Err(err) => { Err(err) => {
return Err(err); return Err(err);
}, }
}; };
if self.internal_buffer_pos >= frame_len as usize { if self.internal_buffer_pos >= frame_len as usize {
@ -183,14 +191,15 @@ impl<I, S> Stream for LengthDelimitedFramedRead<I, S>
self.internal_buffer_pos = 0; self.internal_buffer_pos = 0;
return Ok(Async::Ready(Some(out_data))); return Ok(Async::Ready(Some(out_data)));
} }
}, }
} }
} }
} }
} }
impl<I, S> Sink for LengthDelimitedFramedRead<I, S> impl<I, S> Sink for LengthDelimitedFramedRead<I, S>
where S: Sink where
S: Sink,
{ {
type SinkItem = S::SinkItem; type SinkItem = S::SinkItem;
type SinkError = S::SinkError; type SinkError = S::SinkError;
@ -250,25 +259,34 @@ mod tests {
fn two_bytes_long_packet() { fn two_bytes_long_packet() {
let len = 5000u16; let len = 5000u16;
assert!(len < (1 << 15)); assert!(len < (1 << 15));
let frame = (0 .. len).map(|n| (n & 0xff) as u8).collect::<Vec<_>>(); let frame = (0..len).map(|n| (n & 0xff) as u8).collect::<Vec<_>>();
let mut data = vec![(len & 0x7f) as u8 | 0x80, (len >> 7) as u8]; let mut data = vec![(len & 0x7f) as u8 | 0x80, (len >> 7) as u8];
data.extend(frame.clone().into_iter()); data.extend(frame.clone().into_iter());
let framed = LengthDelimitedFramedRead::<Vec<u8>, _>::new(Cursor::new(data)); let framed = LengthDelimitedFramedRead::<Vec<u8>, _>::new(Cursor::new(data));
let recved = framed.into_future().map(|(m, _)| m).map_err(|_| ()).wait().unwrap(); let recved = framed
.into_future()
.map(|(m, _)| m)
.map_err(|_| ())
.wait()
.unwrap();
assert_eq!(recved.unwrap(), frame); assert_eq!(recved.unwrap(), frame);
} }
#[test] #[test]
fn packet_len_too_long() { fn packet_len_too_long() {
let mut data = vec![0x81, 0x81, 0x1]; let mut data = vec![0x81, 0x81, 0x1];
data.extend((0 .. 16513).map(|_| 0)); data.extend((0..16513).map(|_| 0));
let framed = LengthDelimitedFramedRead::<Vec<u8>, _>::new(Cursor::new(data)); let framed = LengthDelimitedFramedRead::<Vec<u8>, _>::new(Cursor::new(data));
let recved = framed.into_future().map(|(m, _)| m).map_err(|(err, _)| err).wait(); let recved = framed
.into_future()
.map(|(m, _)| m)
.map_err(|(err, _)| err)
.wait();
match recved { match recved {
Err(io_err) => assert_eq!(io_err.kind(), ErrorKind::InvalidData), Err(io_err) => assert_eq!(io_err.kind(), ErrorKind::InvalidData),
_ => panic!() _ => panic!(),
} }
} }
@ -278,7 +296,16 @@ mod tests {
let framed = LengthDelimitedFramedRead::<Vec<u8>, _>::new(Cursor::new(data)); let framed = LengthDelimitedFramedRead::<Vec<u8>, _>::new(Cursor::new(data));
let recved = framed.collect().wait().unwrap(); let recved = framed.collect().wait().unwrap();
assert_eq!(recved, vec![vec![], vec![], vec![9, 8, 7, 6, 5, 4], vec![], vec![9, 8, 7]]); assert_eq!(
recved,
vec![
vec![],
vec![],
vec![9, 8, 7, 6, 5, 4],
vec![],
vec![9, 8, 7],
]
);
} }
#[test] #[test]
@ -289,7 +316,7 @@ mod tests {
let recved = framed.collect().wait(); let recved = framed.collect().wait();
match recved { match recved {
Err(io_err) => assert_eq!(io_err.kind(), ErrorKind::BrokenPipe), Err(io_err) => assert_eq!(io_err.kind(), ErrorKind::BrokenPipe),
_ => panic!() _ => panic!(),
} }
} }
@ -301,7 +328,7 @@ mod tests {
let recved = framed.collect().wait(); let recved = framed.collect().wait();
match recved { match recved {
Err(io_err) => assert_eq!(io_err.kind(), ErrorKind::BrokenPipe), Err(io_err) => assert_eq!(io_err.kind(), ErrorKind::BrokenPipe),
_ => panic!() _ => panic!(),
} }
} }
@ -313,7 +340,7 @@ mod tests {
let recved = framed.collect().wait(); let recved = framed.collect().wait();
match recved { match recved {
Err(io_err) => assert_eq!(io_err.kind(), ErrorKind::BrokenPipe), Err(io_err) => assert_eq!(io_err.kind(), ErrorKind::BrokenPipe),
_ => panic!() _ => panic!(),
} }
} }
} }

View File

@ -1,21 +1,21 @@
// Copyright 2017 Parity Technologies (UK) Ltd. // Copyright 2017 Parity Technologies (UK) Ltd.
// //
// Permission is hereby granted, free of charge, to any person obtaining a // Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"), // copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation // to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, // the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the // and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions: // Software is furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
// TODO: use this once stable ; for now we just copy-paste the content of the README.md // TODO: use this once stable ; for now we just copy-paste the content of the README.md
@ -39,11 +39,11 @@
//! The dialer has two options available: either request the list of protocols that the listener //! The dialer has two options available: either request the list of protocols that the listener
//! supports, or suggest a protocol. If a protocol is suggested, the listener can either accept (by //! supports, or suggest a protocol. If a protocol is suggested, the listener can either accept (by
//! answering with the same protocol name) or refuse the choice (by answering "not available"). //! answering with the same protocol name) or refuse the choice (by answering "not available").
//! //!
//! ## Examples //! ## Examples
//! //!
//! For a dialer: //! For a dialer:
//! //!
//! ```no_run //! ```no_run
//! extern crate bytes; //! extern crate bytes;
//! extern crate futures; //! extern crate futures;
@ -79,7 +79,7 @@
//! ``` //! ```
//! //!
//! For a listener: //! For a listener:
//! //!
//! ```no_run //! ```no_run
//! extern crate bytes; //! extern crate bytes;
//! extern crate futures; //! extern crate futures;

View File

@ -1,21 +1,21 @@
// Copyright 2017 Parity Technologies (UK) Ltd. // Copyright 2017 Parity Technologies (UK) Ltd.
// //
// Permission is hereby granted, free of charge, to any person obtaining a // Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"), // copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation // to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, // the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the // and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions: // Software is furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
//! Contains the `listener_select_proto` code, which allows selecting a protocol thanks to //! Contains the `listener_select_proto` code, which allows selecting a protocol thanks to
@ -47,56 +47,60 @@ use tokio_io::{AsyncRead, AsyncWrite};
/// socket now uses this protocol. /// socket now uses this protocol.
// TODO: remove the Box once -> impl Trait lands // TODO: remove the Box once -> impl Trait lands
pub fn listener_select_proto<'a, R, I, M, P>( pub fn listener_select_proto<'a, R, I, M, P>(
inner: R, inner: R,
protocols: I, protocols: I,
) -> Box<Future<Item = (P, R), Error = ProtocolChoiceError> + 'a> ) -> Box<Future<Item = (P, R), Error = ProtocolChoiceError> + 'a>
where R: AsyncRead + AsyncWrite + 'a, where
I: Iterator<Item = (Bytes, M, P)> + Clone + 'a, R: AsyncRead + AsyncWrite + 'a,
M: FnMut(&Bytes, &Bytes) -> bool + 'a, I: Iterator<Item = (Bytes, M, P)> + Clone + 'a,
P: 'a M: FnMut(&Bytes, &Bytes) -> bool + 'a,
P: 'a,
{ {
let future = Listener::new(inner).from_err().and_then(move |listener| { let future = Listener::new(inner).from_err().and_then(move |listener| {
loop_fn(listener, move |listener| {
let protocols = protocols.clone();
loop_fn(listener, move |listener| { listener
let protocols = protocols.clone(); .into_future()
.map_err(|(e, _)| e.into())
.and_then(move |(message, listener)| match message {
Some(DialerToListenerMessage::ProtocolsListRequest) => {
let msg = ListenerToDialerMessage::ProtocolsListResponse {
list: protocols.map(|(p, _, _)| p).collect(),
};
let fut = listener
.send(msg)
.from_err()
.map(move |listener| (None, listener));
Box::new(fut) as Box<Future<Item = _, Error = ProtocolChoiceError>>
}
Some(DialerToListenerMessage::ProtocolRequest { name }) => {
let mut outcome = None;
let mut send_back = ListenerToDialerMessage::NotAvailable;
for (supported, mut matches, value) in protocols {
if matches(&name, &supported) {
send_back =
ListenerToDialerMessage::ProtocolAck { name: name.clone() };
outcome = Some(value);
break;
}
}
listener.into_future() let fut = listener
.map_err(|(e, _)| e.into()) .send(send_back)
.and_then(move |(message, listener)| match message { .from_err()
Some(DialerToListenerMessage::ProtocolsListRequest) => { .map(move |listener| (outcome, listener));
let msg = ListenerToDialerMessage::ProtocolsListResponse { Box::new(fut) as Box<Future<Item = _, Error = ProtocolChoiceError>>
list: protocols.map(|(p, _, _)| p).collect(), }
}; None => Box::new(err(ProtocolChoiceError::NoProtocolFound)) as Box<_>,
let fut = listener.send(msg).from_err().map(move |listener| (None, listener)); })
Box::new(fut) as Box<Future<Item = _, Error = ProtocolChoiceError>> .map(|(outcome, listener): (_, Listener<R>)| match outcome {
} Some(outcome) => Loop::Break((outcome, listener.into_inner())),
Some(DialerToListenerMessage::ProtocolRequest { name }) => { None => Loop::Continue(listener),
let mut outcome = None; })
let mut send_back = ListenerToDialerMessage::NotAvailable; })
for (supported, mut matches, value) in protocols { });
if matches(&name, &supported) {
send_back = ListenerToDialerMessage::ProtocolAck { name: name.clone() };
outcome = Some(value);
break;
}
}
let fut = listener.send(send_back) // The "Rust doesn't have impl Trait yet" tax.
.from_err() Box::new(future)
.map(move |listener| (outcome, listener));
Box::new(fut) as Box<Future<Item = _, Error = ProtocolChoiceError>>
}
None => {
Box::new(err(ProtocolChoiceError::NoProtocolFound)) as Box<_>
}
})
.map(|(outcome, listener): (_, Listener<R>)| match outcome {
Some(outcome) => Loop::Break((outcome, listener.into_inner())),
None => Loop::Continue(listener),
})
})
});
// The "Rust doesn't have impl Trait yet" tax.
Box::new(future)
} }

View File

@ -1,21 +1,21 @@
// Copyright 2017 Parity Technologies (UK) Ltd. // Copyright 2017 Parity Technologies (UK) Ltd.
// //
// Permission is hereby granted, free of charge, to any person obtaining a // Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"), // copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation // to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, // the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the // and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions: // Software is furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
//! Contains the `Dialer` wrapper, which allows raw communications with a listener. //! Contains the `Dialer` wrapper, which allows raw communications with a listener.
@ -27,7 +27,7 @@ use protocol::DialerToListenerMessage;
use protocol::ListenerToDialerMessage; use protocol::ListenerToDialerMessage;
use protocol::MULTISTREAM_PROTOCOL_WITH_LF; use protocol::MULTISTREAM_PROTOCOL_WITH_LF;
use protocol::MultistreamSelectError; use protocol::MultistreamSelectError;
use std::io::{Cursor, Read, BufRead}; use std::io::{BufRead, Cursor, Read};
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::codec::length_delimited::Builder as LengthDelimitedBuilder; use tokio_io::codec::length_delimited::Builder as LengthDelimitedBuilder;
use tokio_io::codec::length_delimited::FramedWrite as LengthDelimitedFramedWrite; use tokio_io::codec::length_delimited::FramedWrite as LengthDelimitedFramedWrite;
@ -36,181 +36,192 @@ use varint;
/// Wraps around a `AsyncRead+AsyncWrite`. Assumes that we're on the dialer's side. Produces and /// Wraps around a `AsyncRead+AsyncWrite`. Assumes that we're on the dialer's side. Produces and
/// accepts messages. /// accepts messages.
pub struct Dialer<R> { pub struct Dialer<R> {
inner: LengthDelimitedFramedRead<Bytes, LengthDelimitedFramedWrite<R, BytesMut>>, inner: LengthDelimitedFramedRead<Bytes, LengthDelimitedFramedWrite<R, BytesMut>>,
handshake_finished: bool, handshake_finished: bool,
} }
impl<R> Dialer<R> impl<R> Dialer<R>
where R: AsyncRead + AsyncWrite where
R: AsyncRead + AsyncWrite,
{ {
/// Takes ownership of a socket and starts the handshake. If the handshake succeeds, the /// Takes ownership of a socket and starts the handshake. If the handshake succeeds, the
/// future returns a `Dialer`. /// future returns a `Dialer`.
pub fn new<'a>(inner: R) -> Box<Future<Item = Dialer<R>, Error = MultistreamSelectError> + 'a> pub fn new<'a>(inner: R) -> Box<Future<Item = Dialer<R>, Error = MultistreamSelectError> + 'a>
where R: 'a where
{ R: 'a,
let write = LengthDelimitedBuilder::new().length_field_length(1).new_write(inner); {
let inner = LengthDelimitedFramedRead::new(write); let write = LengthDelimitedBuilder::new()
.length_field_length(1)
.new_write(inner);
let inner = LengthDelimitedFramedRead::new(write);
let future = let future = inner
inner.send(BytesMut::from(MULTISTREAM_PROTOCOL_WITH_LF)).from_err().map(|inner| { .send(BytesMut::from(MULTISTREAM_PROTOCOL_WITH_LF))
Dialer { .from_err()
inner: inner, .map(|inner| Dialer {
handshake_finished: false, inner: inner,
} handshake_finished: false,
}); });
Box::new(future) Box::new(future)
} }
/// Grants back the socket. Typically used after a `ProtocolAck` has been received. /// Grants back the socket. Typically used after a `ProtocolAck` has been received.
#[inline] #[inline]
pub fn into_inner(self) -> R { pub fn into_inner(self) -> R {
self.inner.into_inner().into_inner() self.inner.into_inner().into_inner()
} }
} }
impl<R> Sink for Dialer<R> impl<R> Sink for Dialer<R>
where R: AsyncRead + AsyncWrite where
R: AsyncRead + AsyncWrite,
{ {
type SinkItem = DialerToListenerMessage; type SinkItem = DialerToListenerMessage;
type SinkError = MultistreamSelectError; type SinkError = MultistreamSelectError;
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> { fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
match item { match item {
DialerToListenerMessage::ProtocolRequest { name } => { DialerToListenerMessage::ProtocolRequest { name } => {
if !name.starts_with(b"/") { if !name.starts_with(b"/") {
return Err(MultistreamSelectError::WrongProtocolName); return Err(MultistreamSelectError::WrongProtocolName);
} }
let mut protocol = BytesMut::from(name); let mut protocol = BytesMut::from(name);
protocol.extend_from_slice(&[b'\n']); protocol.extend_from_slice(&[b'\n']);
match self.inner.start_send(protocol) { match self.inner.start_send(protocol) {
Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready), Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready),
Ok(AsyncSink::NotReady(mut protocol)) => { Ok(AsyncSink::NotReady(mut protocol)) => {
let protocol_len = protocol.len(); let protocol_len = protocol.len();
protocol.truncate(protocol_len - 1); protocol.truncate(protocol_len - 1);
let protocol = protocol.freeze(); let protocol = protocol.freeze();
Ok(AsyncSink::NotReady( Ok(AsyncSink::NotReady(
DialerToListenerMessage::ProtocolRequest { name: protocol }, DialerToListenerMessage::ProtocolRequest { name: protocol },
)) ))
} }
Err(err) => Err(err.into()), Err(err) => Err(err.into()),
} }
} }
DialerToListenerMessage::ProtocolsListRequest => { DialerToListenerMessage::ProtocolsListRequest => {
match self.inner.start_send(BytesMut::from(&b"ls\n"[..])) { match self.inner.start_send(BytesMut::from(&b"ls\n"[..])) {
Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready), Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready),
Ok(AsyncSink::NotReady(_)) => { Ok(AsyncSink::NotReady(_)) => Ok(AsyncSink::NotReady(
Ok(AsyncSink::NotReady(DialerToListenerMessage::ProtocolsListRequest)) DialerToListenerMessage::ProtocolsListRequest,
} )),
Err(err) => Err(err.into()), Err(err) => Err(err.into()),
} }
} }
} }
} }
#[inline] #[inline]
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
Ok(self.inner.poll_complete()?) Ok(self.inner.poll_complete()?)
} }
} }
impl<R> Stream for Dialer<R> impl<R> Stream for Dialer<R>
where R: AsyncRead + AsyncWrite where
R: AsyncRead + AsyncWrite,
{ {
type Item = ListenerToDialerMessage; type Item = ListenerToDialerMessage;
type Error = MultistreamSelectError; type Error = MultistreamSelectError;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
loop { loop {
let mut frame = match self.inner.poll() { let mut frame = match self.inner.poll() {
Ok(Async::Ready(Some(frame))) => frame, Ok(Async::Ready(Some(frame))) => frame,
Ok(Async::Ready(None)) => return Ok(Async::Ready(None)), Ok(Async::Ready(None)) => return Ok(Async::Ready(None)),
Ok(Async::NotReady) => return Ok(Async::NotReady), Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(err) => return Err(err.into()), Err(err) => return Err(err.into()),
}; };
if !self.handshake_finished { if !self.handshake_finished {
if frame == MULTISTREAM_PROTOCOL_WITH_LF { if frame == MULTISTREAM_PROTOCOL_WITH_LF {
self.handshake_finished = true; self.handshake_finished = true;
continue; continue;
} else { } else {
return Err(MultistreamSelectError::FailedHandshake); return Err(MultistreamSelectError::FailedHandshake);
} }
} }
if frame.get(0) == Some(&b'/') && frame.last() == Some(&b'\n') { if frame.get(0) == Some(&b'/') && frame.last() == Some(&b'\n') {
let frame_len = frame.len(); let frame_len = frame.len();
let protocol = frame.split_to(frame_len - 1); let protocol = frame.split_to(frame_len - 1);
return Ok( return Ok(Async::Ready(Some(ListenerToDialerMessage::ProtocolAck {
Async::Ready(Some(ListenerToDialerMessage::ProtocolAck { name: protocol })), name: protocol,
); })));
} else if frame == &b"na\n"[..] {
return Ok(Async::Ready(Some(ListenerToDialerMessage::NotAvailable)));
} else {
// A varint number of protocols
let mut reader = Cursor::new(frame);
let num_protocols: usize = varint::decode(reader.by_ref())?;
} else if frame == &b"na\n"[..] { let mut iter = BufRead::split(reader, b'\r');
return Ok(Async::Ready(Some(ListenerToDialerMessage::NotAvailable))); if !iter.next()
.ok_or(MultistreamSelectError::UnknownMessage)??
.is_empty()
{
return Err(MultistreamSelectError::UnknownMessage);
}
} else { let mut out = Vec::with_capacity(num_protocols);
// A varint number of protocols for proto in iter.by_ref().take(num_protocols) {
let mut reader = Cursor::new(frame); let mut proto = proto?;
let num_protocols: usize = varint::decode(reader.by_ref())?; let poped = proto.pop(); // Pop the `\n`
if poped != Some(b'\n') {
return Err(MultistreamSelectError::UnknownMessage);
}
out.push(Bytes::from(proto));
}
let mut iter = BufRead::split(reader, b'\r'); // Making sure that the number of protocols was correct.
if !iter.next().ok_or(MultistreamSelectError::UnknownMessage)??.is_empty() { if iter.next().is_some() || out.len() != num_protocols {
return Err(MultistreamSelectError::UnknownMessage); return Err(MultistreamSelectError::UnknownMessage);
} }
let mut out = Vec::with_capacity(num_protocols); return Ok(Async::Ready(Some(
for proto in iter.by_ref().take(num_protocols) { ListenerToDialerMessage::ProtocolsListResponse { list: out },
let mut proto = proto?; )));
let poped = proto.pop(); // Pop the `\n` }
if poped != Some(b'\n') { }
return Err(MultistreamSelectError::UnknownMessage); }
}
out.push(Bytes::from(proto));
}
// Making sure that the number of protocols was correct.
if iter.next().is_some() || out.len() != num_protocols {
return Err(MultistreamSelectError::UnknownMessage);
}
return Ok(Async::Ready(Some(ListenerToDialerMessage::ProtocolsListResponse {
list: out
})));
}
}
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate tokio_core; extern crate tokio_core;
use bytes::Bytes; use bytes::Bytes;
use futures::{Sink, Stream}; use futures::{Sink, Stream};
use futures::Future; use futures::Future;
use protocol::{Dialer, DialerToListenerMessage, MultistreamSelectError}; use protocol::{Dialer, DialerToListenerMessage, MultistreamSelectError};
use self::tokio_core::net::{TcpListener, TcpStream}; use self::tokio_core::net::{TcpListener, TcpStream};
use self::tokio_core::reactor::Core; use self::tokio_core::reactor::Core;
#[test] #[test]
fn wrong_proto_name() { fn wrong_proto_name() {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap(); let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
let listener_addr = listener.local_addr().unwrap(); let listener_addr = listener.local_addr().unwrap();
let server = listener.incoming().into_future().map(|_| ()).map_err(|(e, _)| e.into()); let server = listener
.incoming()
.into_future()
.map(|_| ())
.map_err(|(e, _)| e.into());
let client = TcpStream::connect(&listener_addr, &core.handle()) let client = TcpStream::connect(&listener_addr, &core.handle())
.from_err() .from_err()
.and_then(move |stream| Dialer::new(stream)) .and_then(move |stream| Dialer::new(stream))
.and_then(move |dialer| { .and_then(move |dialer| {
let p = Bytes::from("invalid_name"); let p = Bytes::from("invalid_name");
dialer.send(DialerToListenerMessage::ProtocolRequest { name: p }) dialer.send(DialerToListenerMessage::ProtocolRequest { name: p })
}); });
match core.run(server.join(client)) { match core.run(server.join(client)) {
Err(MultistreamSelectError::WrongProtocolName) => (), Err(MultistreamSelectError::WrongProtocolName) => (),
_ => panic!(), _ => panic!(),
} }
} }
} }

View File

@ -1,21 +1,21 @@
// Copyright 2017 Parity Technologies (UK) Ltd. // Copyright 2017 Parity Technologies (UK) Ltd.
// //
// Permission is hereby granted, free of charge, to any person obtaining a // Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"), // copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation // to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, // the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the // and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions: // Software is furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
//! Contains the error structs for the low-level protocol handling. //! Contains the error structs for the low-level protocol handling.
@ -28,72 +28,66 @@ use varint;
/// Error at the multistream-select layer of communication. /// Error at the multistream-select layer of communication.
#[derive(Debug)] #[derive(Debug)]
pub enum MultistreamSelectError { pub enum MultistreamSelectError {
/// I/O error. /// I/O error.
IoError(io::Error), IoError(io::Error),
/// The remote doesn't use the same multistream-select protocol as we do. /// The remote doesn't use the same multistream-select protocol as we do.
FailedHandshake, FailedHandshake,
/// Received an unknown message from the remote. /// Received an unknown message from the remote.
UnknownMessage, UnknownMessage,
/// Protocol names must always start with `/`, otherwise this error is returned. /// Protocol names must always start with `/`, otherwise this error is returned.
WrongProtocolName, WrongProtocolName,
/// Failure to parse variable-length integer. /// Failure to parse variable-length integer.
// TODO: we don't include the actual error, because that would remove Send from the enum // TODO: we don't include the actual error, because that would remove Send from the enum
VarintParseError(String), VarintParseError(String),
} }
impl From<io::Error> for MultistreamSelectError { impl From<io::Error> for MultistreamSelectError {
#[inline] #[inline]
fn from(err: io::Error) -> MultistreamSelectError { fn from(err: io::Error) -> MultistreamSelectError {
MultistreamSelectError::IoError(err) MultistreamSelectError::IoError(err)
} }
} }
impl From<varint::Error> for MultistreamSelectError { impl From<varint::Error> for MultistreamSelectError {
#[inline] #[inline]
fn from(err: varint::Error) -> MultistreamSelectError { fn from(err: varint::Error) -> MultistreamSelectError {
MultistreamSelectError::VarintParseError(err.to_string()) MultistreamSelectError::VarintParseError(err.to_string())
} }
} }
impl error::Error for MultistreamSelectError { impl error::Error for MultistreamSelectError {
#[inline] #[inline]
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
MultistreamSelectError::IoError(_) => { MultistreamSelectError::IoError(_) => "I/O error",
"I/O error" MultistreamSelectError::FailedHandshake => {
}, "the remote doesn't use the same multistream-select protocol as we do"
MultistreamSelectError::FailedHandshake => { }
"the remote doesn't use the same multistream-select protocol as we do" MultistreamSelectError::UnknownMessage => "received an unknown message from the remote",
}, MultistreamSelectError::WrongProtocolName => {
MultistreamSelectError::UnknownMessage => { "protocol names must always start with `/`, otherwise this error is returned"
"received an unknown message from the remote" }
}, MultistreamSelectError::VarintParseError(_) => {
MultistreamSelectError::WrongProtocolName => { "failure to parse variable-length integer"
"protocol names must always start with `/`, otherwise this error is returned" }
}, }
MultistreamSelectError::VarintParseError(_) => { }
"failure to parse variable-length integer"
},
}
}
fn cause(&self) -> Option<&error::Error> { fn cause(&self) -> Option<&error::Error> {
match *self { match *self {
MultistreamSelectError::IoError(ref err) => { MultistreamSelectError::IoError(ref err) => Some(err),
Some(err) _ => None,
} }
_ => None, }
}
}
} }
impl fmt::Display for MultistreamSelectError { impl fmt::Display for MultistreamSelectError {
#[inline] #[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self)) write!(fmt, "{}", error::Error::description(self))
} }
} }

View File

@ -1,21 +1,21 @@
// Copyright 2017 Parity Technologies (UK) Ltd. // Copyright 2017 Parity Technologies (UK) Ltd.
// //
// Permission is hereby granted, free of charge, to any person obtaining a // Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"), // copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation // to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, // the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the // and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions: // Software is furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
//! Contains the `Listener` wrapper, which allows raw communications with a dialer. //! Contains the `Listener` wrapper, which allows raw communications with a dialer.
@ -35,176 +35,186 @@ use varint;
/// Wraps around a `AsyncRead+AsyncWrite`. Assumes that we're on the listener's side. Produces and /// Wraps around a `AsyncRead+AsyncWrite`. Assumes that we're on the listener's side. Produces and
/// accepts messages. /// accepts messages.
pub struct Listener<R> { pub struct Listener<R> {
inner: LengthDelimitedFramedRead<Bytes, LengthDelimitedFramedWrite<R, BytesMut>>, inner: LengthDelimitedFramedRead<Bytes, LengthDelimitedFramedWrite<R, BytesMut>>,
} }
impl<R> Listener<R> impl<R> Listener<R>
where R: AsyncRead + AsyncWrite where
R: AsyncRead + AsyncWrite,
{ {
/// Takes ownership of a socket and starts the handshake. If the handshake succeeds, the /// Takes ownership of a socket and starts the handshake. If the handshake succeeds, the
/// future returns a `Listener`. /// future returns a `Listener`.
pub fn new<'a>(inner: R) -> Box<Future<Item = Listener<R>, Error = MultistreamSelectError> + 'a> pub fn new<'a>(inner: R) -> Box<Future<Item = Listener<R>, Error = MultistreamSelectError> + 'a>
where R: 'a where
{ R: 'a,
let write = LengthDelimitedBuilder::new().length_field_length(1).new_write(inner); {
let inner = LengthDelimitedFramedRead::<Bytes, _>::new(write); let write = LengthDelimitedBuilder::new()
.length_field_length(1)
.new_write(inner);
let inner = LengthDelimitedFramedRead::<Bytes, _>::new(write);
let future = inner.into_future() let future = inner
.map_err(|(e, _)| e.into()) .into_future()
.and_then(|(msg, rest)| { .map_err(|(e, _)| e.into())
if msg.as_ref().map(|b| &b[..]) != Some(MULTISTREAM_PROTOCOL_WITH_LF) { .and_then(|(msg, rest)| {
return Err(MultistreamSelectError::FailedHandshake); if msg.as_ref().map(|b| &b[..]) != Some(MULTISTREAM_PROTOCOL_WITH_LF) {
} return Err(MultistreamSelectError::FailedHandshake);
Ok(rest) }
}) Ok(rest)
.and_then(|socket| { })
socket.send(BytesMut::from(MULTISTREAM_PROTOCOL_WITH_LF)).from_err() .and_then(|socket| {
}) socket
.map(|inner| Listener { inner: inner }); .send(BytesMut::from(MULTISTREAM_PROTOCOL_WITH_LF))
.from_err()
})
.map(|inner| Listener { inner: inner });
Box::new(future) Box::new(future)
} }
/// Grants back the socket. Typically used after a `ProtocolRequest` has been received and a /// Grants back the socket. Typically used after a `ProtocolRequest` has been received and a
/// `ProtocolAck` has been sent back. /// `ProtocolAck` has been sent back.
#[inline] #[inline]
pub fn into_inner(self) -> R { pub fn into_inner(self) -> R {
self.inner.into_inner().into_inner() self.inner.into_inner().into_inner()
} }
} }
impl<R> Sink for Listener<R> impl<R> Sink for Listener<R>
where R: AsyncRead + AsyncWrite where
R: AsyncRead + AsyncWrite,
{ {
type SinkItem = ListenerToDialerMessage; type SinkItem = ListenerToDialerMessage;
type SinkError = MultistreamSelectError; type SinkError = MultistreamSelectError;
#[inline] #[inline]
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> { fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
match item { match item {
ListenerToDialerMessage::ProtocolAck { name } => { ListenerToDialerMessage::ProtocolAck { name } => {
if !name.starts_with(b"/") { if !name.starts_with(b"/") {
return Err(MultistreamSelectError::WrongProtocolName); return Err(MultistreamSelectError::WrongProtocolName);
} }
let mut protocol = BytesMut::from(name); let mut protocol = BytesMut::from(name);
protocol.extend_from_slice(&[b'\n']); protocol.extend_from_slice(&[b'\n']);
match self.inner.start_send(protocol) { match self.inner.start_send(protocol) {
Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready), Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready),
Ok(AsyncSink::NotReady(mut protocol)) => { Ok(AsyncSink::NotReady(mut protocol)) => {
let protocol_len = protocol.len(); let protocol_len = protocol.len();
protocol.truncate(protocol_len - 1); protocol.truncate(protocol_len - 1);
let protocol = protocol.freeze(); let protocol = protocol.freeze();
Ok( Ok(AsyncSink::NotReady(ListenerToDialerMessage::ProtocolAck {
AsyncSink::NotReady(ListenerToDialerMessage::ProtocolAck { name: protocol }), name: protocol,
) }))
} }
Err(err) => Err(err.into()), Err(err) => Err(err.into()),
} }
} }
ListenerToDialerMessage::NotAvailable => { ListenerToDialerMessage::NotAvailable => {
match self.inner.start_send(BytesMut::from(&b"na\n"[..])) { match self.inner.start_send(BytesMut::from(&b"na\n"[..])) {
Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready), Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready),
Ok(AsyncSink::NotReady(_)) => { Ok(AsyncSink::NotReady(_)) => {
Ok(AsyncSink::NotReady(ListenerToDialerMessage::NotAvailable)) Ok(AsyncSink::NotReady(ListenerToDialerMessage::NotAvailable))
} }
Err(err) => Err(err.into()), Err(err) => Err(err.into()),
} }
} }
ListenerToDialerMessage::ProtocolsListResponse { list } => { ListenerToDialerMessage::ProtocolsListResponse { list } => {
use std::iter; use std::iter;
let mut out_msg = varint::encode(list.len()); let mut out_msg = varint::encode(list.len());
for elem in list.iter() { for elem in list.iter() {
out_msg.extend(iter::once(b'\r')); out_msg.extend(iter::once(b'\r'));
out_msg.extend_from_slice(elem); out_msg.extend_from_slice(elem);
out_msg.extend(iter::once(b'\n')); out_msg.extend(iter::once(b'\n'));
} }
match self.inner.start_send(BytesMut::from(out_msg)) { match self.inner.start_send(BytesMut::from(out_msg)) {
Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready), Ok(AsyncSink::Ready) => Ok(AsyncSink::Ready),
Ok(AsyncSink::NotReady(_)) => { Ok(AsyncSink::NotReady(_)) => {
let m = ListenerToDialerMessage::ProtocolsListResponse { list }; let m = ListenerToDialerMessage::ProtocolsListResponse { list };
Ok(AsyncSink::NotReady(m)) Ok(AsyncSink::NotReady(m))
} }
Err(err) => Err(err.into()), Err(err) => Err(err.into()),
} }
} }
} }
} }
#[inline] #[inline]
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
Ok(self.inner.poll_complete()?) Ok(self.inner.poll_complete()?)
} }
} }
impl<R> Stream for Listener<R> impl<R> Stream for Listener<R>
where R: AsyncRead + AsyncWrite where
R: AsyncRead + AsyncWrite,
{ {
type Item = DialerToListenerMessage; type Item = DialerToListenerMessage;
type Error = MultistreamSelectError; type Error = MultistreamSelectError;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
loop { loop {
let mut frame = match self.inner.poll() { let mut frame = match self.inner.poll() {
Ok(Async::Ready(Some(frame))) => frame, Ok(Async::Ready(Some(frame))) => frame,
Ok(Async::Ready(None)) => return Ok(Async::Ready(None)), Ok(Async::Ready(None)) => return Ok(Async::Ready(None)),
Ok(Async::NotReady) => return Ok(Async::NotReady), Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(err) => return Err(err.into()), Err(err) => return Err(err.into()),
}; };
if frame.get(0) == Some(&b'/') && frame.last() == Some(&b'\n') { if frame.get(0) == Some(&b'/') && frame.last() == Some(&b'\n') {
let frame_len = frame.len(); let frame_len = frame.len();
let protocol = frame.split_to(frame_len - 1); let protocol = frame.split_to(frame_len - 1);
return Ok(Async::Ready( return Ok(Async::Ready(Some(
Some(DialerToListenerMessage::ProtocolRequest { name: protocol }), DialerToListenerMessage::ProtocolRequest { name: protocol },
)); )));
} else if frame == &b"ls\n"[..] {
} else if frame == &b"ls\n"[..] { return Ok(Async::Ready(Some(
return Ok(Async::Ready(Some(DialerToListenerMessage::ProtocolsListRequest))); DialerToListenerMessage::ProtocolsListRequest,
)));
} else { } else {
return Err(MultistreamSelectError::UnknownMessage); return Err(MultistreamSelectError::UnknownMessage);
} }
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate tokio_core; extern crate tokio_core;
use bytes::Bytes; use bytes::Bytes;
use futures::{Sink, Stream}; use futures::{Sink, Stream};
use futures::Future; use futures::Future;
use protocol::{Dialer, Listener, ListenerToDialerMessage, MultistreamSelectError}; use protocol::{Dialer, Listener, ListenerToDialerMessage, MultistreamSelectError};
use self::tokio_core::net::{TcpListener, TcpStream}; use self::tokio_core::net::{TcpListener, TcpStream};
use self::tokio_core::reactor::Core; use self::tokio_core::reactor::Core;
#[test] #[test]
fn wrong_proto_name() { fn wrong_proto_name() {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap(); let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
let listener_addr = listener.local_addr().unwrap(); let listener_addr = listener.local_addr().unwrap();
let server = listener.incoming() let server = listener
.into_future() .incoming()
.map_err(|(e, _)| e.into()) .into_future()
.and_then(move |(connec, _)| Listener::new(connec.unwrap().0)) .map_err(|(e, _)| e.into())
.and_then(|listener| { .and_then(move |(connec, _)| Listener::new(connec.unwrap().0))
let proto_name = Bytes::from("invalid-proto"); .and_then(|listener| {
listener.send(ListenerToDialerMessage::ProtocolAck { name: proto_name }) let proto_name = Bytes::from("invalid-proto");
}); listener.send(ListenerToDialerMessage::ProtocolAck { name: proto_name })
});
let client = TcpStream::connect(&listener_addr, &core.handle()) let client = TcpStream::connect(&listener_addr, &core.handle())
.from_err() .from_err()
.and_then(move |stream| Dialer::new(stream)); .and_then(move |stream| Dialer::new(stream));
match core.run(server.join(client)) { match core.run(server.join(client)) {
Err(MultistreamSelectError::WrongProtocolName) => (), Err(MultistreamSelectError::WrongProtocolName) => (),
_ => panic!(), _ => panic!(),
} }
} }
} }

View File

@ -1,21 +1,21 @@
// Copyright 2017 Parity Technologies (UK) Ltd. // Copyright 2017 Parity Technologies (UK) Ltd.
// //
// Permission is hereby granted, free of charge, to any person obtaining a // Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"), // copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation // to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, // the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the // and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions: // Software is furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
//! Contains lower-level structs to handle the multistream protocol. //! Contains lower-level structs to handle the multistream protocol.
@ -35,33 +35,33 @@ pub use self::listener::Listener;
/// Message sent from the dialer to the listener. /// Message sent from the dialer to the listener.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum DialerToListenerMessage { pub enum DialerToListenerMessage {
/// The dialer wants us to use a protocol. /// The dialer wants us to use a protocol.
/// ///
/// If this is accepted (by receiving back a `ProtocolAck`), then we immediately start /// If this is accepted (by receiving back a `ProtocolAck`), then we immediately start
/// communicating in the new protocol. /// communicating in the new protocol.
ProtocolRequest { ProtocolRequest {
/// Name of the protocol. /// Name of the protocol.
name: Bytes, name: Bytes,
}, },
/// The dialer requested the list of protocols that the listener supports. /// The dialer requested the list of protocols that the listener supports.
ProtocolsListRequest, ProtocolsListRequest,
} }
/// Message sent from the listener to the dialer. /// Message sent from the listener to the dialer.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum ListenerToDialerMessage { pub enum ListenerToDialerMessage {
/// The protocol requested by the dialer is accepted. The socket immediately starts using the /// The protocol requested by the dialer is accepted. The socket immediately starts using the
/// new protocol. /// new protocol.
ProtocolAck { name: Bytes }, ProtocolAck { name: Bytes },
/// The protocol requested by the dialer is not supported or available. /// The protocol requested by the dialer is not supported or available.
NotAvailable, NotAvailable,
/// Response to the request for the list of protocols. /// Response to the request for the list of protocols.
ProtocolsListResponse { ProtocolsListResponse {
/// The list of protocols. /// The list of protocols.
// TODO: use some sort of iterator // TODO: use some sort of iterator
list: Vec<Bytes>, list: Vec<Bytes>,
}, },
} }

View File

@ -1,21 +1,21 @@
// Copyright 2017 Parity Technologies (UK) Ltd. // Copyright 2017 Parity Technologies (UK) Ltd.
// //
// Permission is hereby granted, free of charge, to any person obtaining a // Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"), // copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation // to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, // the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the // and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions: // Software is furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
//! Contains the unit tests of the library. //! Contains the unit tests of the library.
@ -24,190 +24,192 @@
extern crate tokio_core; extern crate tokio_core;
use {listener_select_proto, dialer_select_proto}; use {dialer_select_proto, listener_select_proto};
use ProtocolChoiceError; use ProtocolChoiceError;
use bytes::Bytes; use bytes::Bytes;
use dialer_select::{dialer_select_proto_parallel, dialer_select_proto_serial}; use dialer_select::{dialer_select_proto_parallel, dialer_select_proto_serial};
use futures::{Sink, Stream}; use futures::{Sink, Stream};
use futures::Future; use futures::Future;
use protocol::{Dialer, Listener, DialerToListenerMessage, ListenerToDialerMessage}; use protocol::{Dialer, DialerToListenerMessage, Listener, ListenerToDialerMessage};
use self::tokio_core::net::TcpListener; use self::tokio_core::net::TcpListener;
use self::tokio_core::net::TcpStream; use self::tokio_core::net::TcpStream;
use self::tokio_core::reactor::Core; use self::tokio_core::reactor::Core;
#[test] #[test]
fn negotiate_with_self_succeeds() { fn negotiate_with_self_succeeds() {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap(); let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
let listener_addr = listener.local_addr().unwrap(); let listener_addr = listener.local_addr().unwrap();
let server = listener.incoming() let server = listener
.into_future() .incoming()
.map_err(|(e, _)| e.into()) .into_future()
.and_then(move |(connec, _)| Listener::new(connec.unwrap().0)) .map_err(|(e, _)| e.into())
.and_then(|l| l.into_future().map_err(|(e, _)| e)) .and_then(move |(connec, _)| Listener::new(connec.unwrap().0))
.and_then(|(msg, rest)| { .and_then(|l| l.into_future().map_err(|(e, _)| e))
let proto = match msg { .and_then(|(msg, rest)| {
Some(DialerToListenerMessage::ProtocolRequest { name }) => name, let proto = match msg {
_ => panic!(), Some(DialerToListenerMessage::ProtocolRequest { name }) => name,
}; _ => panic!(),
rest.send(ListenerToDialerMessage::ProtocolAck { name: proto }) };
}); rest.send(ListenerToDialerMessage::ProtocolAck { name: proto })
});
let client = TcpStream::connect(&listener_addr, &core.handle()) let client = TcpStream::connect(&listener_addr, &core.handle())
.from_err() .from_err()
.and_then(move |stream| Dialer::new(stream)) .and_then(move |stream| Dialer::new(stream))
.and_then(move |dialer| { .and_then(move |dialer| {
let p = Bytes::from("/hello/1.0.0"); let p = Bytes::from("/hello/1.0.0");
dialer.send(DialerToListenerMessage::ProtocolRequest { name: p }) dialer.send(DialerToListenerMessage::ProtocolRequest { name: p })
}) })
.and_then(move |dialer| dialer.into_future().map_err(|(e, _)| e)) .and_then(move |dialer| dialer.into_future().map_err(|(e, _)| e))
.and_then(move |(msg, _)| { .and_then(move |(msg, _)| {
let proto = match msg { let proto = match msg {
Some(ListenerToDialerMessage::ProtocolAck { name }) => name, Some(ListenerToDialerMessage::ProtocolAck { name }) => name,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(proto, "/hello/1.0.0"); assert_eq!(proto, "/hello/1.0.0");
Ok(()) Ok(())
}); });
core.run(server.join(client)).unwrap(); core.run(server.join(client)).unwrap();
} }
#[test] #[test]
fn select_proto_basic() { fn select_proto_basic() {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap(); let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
let listener_addr = listener.local_addr().unwrap(); let listener_addr = listener.local_addr().unwrap();
let server = listener.incoming() let server = listener
.into_future() .incoming()
.map(|s| s.0.unwrap().0) .into_future()
.map_err(|(e, _)| e.into()) .map(|s| s.0.unwrap().0)
.and_then(move |connec| { .map_err(|(e, _)| e.into())
let protos = vec![ .and_then(move |connec| {
(Bytes::from("/proto1"), <Bytes as PartialEq>::eq, 0), let protos = vec![
(Bytes::from("/proto2"), <Bytes as PartialEq>::eq, 1), (Bytes::from("/proto1"), <Bytes as PartialEq>::eq, 0),
] (Bytes::from("/proto2"), <Bytes as PartialEq>::eq, 1),
.into_iter(); ].into_iter();
listener_select_proto(connec, protos).map(|r| r.0) listener_select_proto(connec, protos).map(|r| r.0)
}); });
let client = let client = TcpStream::connect(&listener_addr, &core.handle())
TcpStream::connect(&listener_addr, &core.handle()).from_err().and_then(move |connec| { .from_err()
let protos = vec![ .and_then(move |connec| {
(Bytes::from("/proto3"), <Bytes as PartialEq>::eq, 2), let protos = vec![
(Bytes::from("/proto2"), <Bytes as PartialEq>::eq, 3), (Bytes::from("/proto3"), <Bytes as PartialEq>::eq, 2),
] (Bytes::from("/proto2"), <Bytes as PartialEq>::eq, 3),
.into_iter(); ].into_iter();
dialer_select_proto(connec, protos).map(|r| r.0) dialer_select_proto(connec, protos).map(|r| r.0)
}); });
let (dialer_chosen, listener_chosen) = core.run(client.join(server)).unwrap(); let (dialer_chosen, listener_chosen) = core.run(client.join(server)).unwrap();
assert_eq!(dialer_chosen, 3); assert_eq!(dialer_chosen, 3);
assert_eq!(listener_chosen, 1); assert_eq!(listener_chosen, 1);
} }
#[test] #[test]
fn no_protocol_found() { fn no_protocol_found() {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap(); let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
let listener_addr = listener.local_addr().unwrap(); let listener_addr = listener.local_addr().unwrap();
let server = listener.incoming() let server = listener
.into_future() .incoming()
.map(|s| s.0.unwrap().0) .into_future()
.map_err(|(e, _)| e.into()) .map(|s| s.0.unwrap().0)
.and_then(move |connec| { .map_err(|(e, _)| e.into())
let protos = vec![ .and_then(move |connec| {
(Bytes::from("/proto1"), <Bytes as PartialEq>::eq, 1), let protos = vec![
(Bytes::from("/proto2"), <Bytes as PartialEq>::eq, 2), (Bytes::from("/proto1"), <Bytes as PartialEq>::eq, 1),
] (Bytes::from("/proto2"), <Bytes as PartialEq>::eq, 2),
.into_iter(); ].into_iter();
listener_select_proto(connec, protos).map(|r| r.0) listener_select_proto(connec, protos).map(|r| r.0)
}); });
let client = let client = TcpStream::connect(&listener_addr, &core.handle())
TcpStream::connect(&listener_addr, &core.handle()).from_err().and_then(move |connec| { .from_err()
let protos = vec![ .and_then(move |connec| {
(Bytes::from("/proto3"), <Bytes as PartialEq>::eq, 3), let protos = vec![
(Bytes::from("/proto4"), <Bytes as PartialEq>::eq, 4), (Bytes::from("/proto3"), <Bytes as PartialEq>::eq, 3),
] (Bytes::from("/proto4"), <Bytes as PartialEq>::eq, 4),
.into_iter(); ].into_iter();
dialer_select_proto(connec, protos).map(|r| r.0) dialer_select_proto(connec, protos).map(|r| r.0)
}); });
match core.run(client.join(server)) { match core.run(client.join(server)) {
Err(ProtocolChoiceError::NoProtocolFound) => (), Err(ProtocolChoiceError::NoProtocolFound) => (),
_ => panic!(), _ => panic!(),
} }
} }
#[test] #[test]
fn select_proto_parallel() { fn select_proto_parallel() {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap(); let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
let listener_addr = listener.local_addr().unwrap(); let listener_addr = listener.local_addr().unwrap();
let server = listener.incoming() let server = listener
.into_future() .incoming()
.map(|s| s.0.unwrap().0) .into_future()
.map_err(|(e, _)| e.into()) .map(|s| s.0.unwrap().0)
.and_then(move |connec| { .map_err(|(e, _)| e.into())
let protos = vec![ .and_then(move |connec| {
(Bytes::from("/proto1"), <Bytes as PartialEq>::eq, 0), let protos = vec![
(Bytes::from("/proto2"), <Bytes as PartialEq>::eq, 1), (Bytes::from("/proto1"), <Bytes as PartialEq>::eq, 0),
] (Bytes::from("/proto2"), <Bytes as PartialEq>::eq, 1),
.into_iter(); ].into_iter();
listener_select_proto(connec, protos).map(|r| r.0) listener_select_proto(connec, protos).map(|r| r.0)
}); });
let client = let client = TcpStream::connect(&listener_addr, &core.handle())
TcpStream::connect(&listener_addr, &core.handle()).from_err().and_then(move |connec| { .from_err()
let protos = vec![ .and_then(move |connec| {
(Bytes::from("/proto3"), <Bytes as PartialEq>::eq, 2), let protos = vec![
(Bytes::from("/proto2"), <Bytes as PartialEq>::eq, 3), (Bytes::from("/proto3"), <Bytes as PartialEq>::eq, 2),
] (Bytes::from("/proto2"), <Bytes as PartialEq>::eq, 3),
.into_iter(); ].into_iter();
dialer_select_proto_parallel(connec, protos).map(|r| r.0) dialer_select_proto_parallel(connec, protos).map(|r| r.0)
}); });
let (dialer_chosen, listener_chosen) = core.run(client.join(server)).unwrap(); let (dialer_chosen, listener_chosen) = core.run(client.join(server)).unwrap();
assert_eq!(dialer_chosen, 3); assert_eq!(dialer_chosen, 3);
assert_eq!(listener_chosen, 1); assert_eq!(listener_chosen, 1);
} }
#[test] #[test]
fn select_proto_serial() { fn select_proto_serial() {
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap(); let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &core.handle()).unwrap();
let listener_addr = listener.local_addr().unwrap(); let listener_addr = listener.local_addr().unwrap();
let server = listener.incoming() let server = listener
.into_future() .incoming()
.map(|s| s.0.unwrap().0) .into_future()
.map_err(|(e, _)| e.into()) .map(|s| s.0.unwrap().0)
.and_then(move |connec| { .map_err(|(e, _)| e.into())
let protos = vec![ .and_then(move |connec| {
(Bytes::from("/proto1"), <Bytes as PartialEq>::eq, 0), let protos = vec![
(Bytes::from("/proto2"), <Bytes as PartialEq>::eq, 1), (Bytes::from("/proto1"), <Bytes as PartialEq>::eq, 0),
] (Bytes::from("/proto2"), <Bytes as PartialEq>::eq, 1),
.into_iter(); ].into_iter();
listener_select_proto(connec, protos).map(|r| r.0) listener_select_proto(connec, protos).map(|r| r.0)
}); });
let client = let client = TcpStream::connect(&listener_addr, &core.handle())
TcpStream::connect(&listener_addr, &core.handle()).from_err().and_then(move |connec| { .from_err()
let protos = vec![(Bytes::from("/proto3"), 2), (Bytes::from("/proto2"), 3)].into_iter(); .and_then(move |connec| {
dialer_select_proto_serial(connec, protos).map(|r| r.0) let protos = vec![(Bytes::from("/proto3"), 2), (Bytes::from("/proto2"), 3)].into_iter();
}); dialer_select_proto_serial(connec, protos).map(|r| r.0)
});
let (dialer_chosen, listener_chosen) = core.run(client.join(server)).unwrap(); let (dialer_chosen, listener_chosen) = core.run(client.join(server)).unwrap();
assert_eq!(dialer_chosen, 3); assert_eq!(dialer_chosen, 3);
assert_eq!(listener_chosen, 1); assert_eq!(listener_chosen, 1);
} }

View File

@ -1,4 +1,4 @@
use std::{net, fmt, error, io, num, string}; use std::{error, fmt, io, net, num, string};
use cid; use cid;
use byteorder; use byteorder;
@ -35,7 +35,7 @@ impl error::Error for Error {
fn cause(&self) -> Option<&error::Error> { fn cause(&self) -> Option<&error::Error> {
match *self { match *self {
Error::ParsingError(ref err) => Some(&**err), Error::ParsingError(ref err) => Some(&**err),
_ => None _ => None,
} }
} }
} }

View File

@ -9,12 +9,12 @@ extern crate integer_encoding;
mod protocol; mod protocol;
mod errors; mod errors;
pub use errors::{Result, Error}; pub use errors::{Error, Result};
pub use protocol::{ProtocolId, ProtocolArgSize, AddrComponent}; pub use protocol::{AddrComponent, ProtocolArgSize, ProtocolId};
use std::fmt; use std::fmt;
use std::iter::FromIterator; use std::iter::FromIterator;
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::str::FromStr; use std::str::FromStr;
/// Representation of a Multiaddr. /// Representation of a Multiaddr.
@ -135,7 +135,9 @@ impl Multiaddr {
/// ///
#[inline] #[inline]
pub fn append(&mut self, component: AddrComponent) { pub fn append(&mut self, component: AddrComponent) {
component.write_bytes(&mut self.bytes).expect("writing to a Vec never fails") component
.write_bytes(&mut self.bytes)
.expect("writing to a Vec never fails")
} }
/// Remove the outermost address. /// Remove the outermost address.
@ -189,7 +191,9 @@ impl Multiaddr {
} }
if !matches { if !matches {
return Ok(Multiaddr { bytes: self.bytes.clone() }); return Ok(Multiaddr {
bytes: self.bytes.clone(),
});
} }
let mut bytes = self.bytes.clone(); let mut bytes = self.bytes.clone();
@ -199,14 +203,14 @@ impl Multiaddr {
} }
/// Returns the components of this multiaddress. /// Returns the components of this multiaddress.
/// ///
/// ``` /// ```
/// use std::net::Ipv4Addr; /// use std::net::Ipv4Addr;
/// use multiaddr::AddrComponent; /// use multiaddr::AddrComponent;
/// use multiaddr::Multiaddr; /// use multiaddr::Multiaddr;
/// ///
/// let address: Multiaddr = "/ip4/127.0.0.1/udt/sctp/5678".parse().unwrap(); /// let address: Multiaddr = "/ip4/127.0.0.1/udt/sctp/5678".parse().unwrap();
/// ///
/// let components = address.iter().collect::<Vec<_>>(); /// let components = address.iter().collect::<Vec<_>>();
/// assert_eq!(components[0], AddrComponent::IP4(Ipv4Addr::new(127, 0, 0, 1))); /// assert_eq!(components[0], AddrComponent::IP4(Ipv4Addr::new(127, 0, 0, 1)));
/// assert_eq!(components[1], AddrComponent::UDT); /// assert_eq!(components[1], AddrComponent::UDT);
@ -218,13 +222,13 @@ impl Multiaddr {
Iter(&self.bytes) Iter(&self.bytes)
} }
/// Pops the last `AddrComponent` of this multiaddr, or `None` if the multiaddr is empty. /// Pops the last `AddrComponent` of this multiaddr, or `None` if the multiaddr is empty.
/// ``` /// ```
/// use multiaddr::AddrComponent; /// use multiaddr::AddrComponent;
/// use multiaddr::Multiaddr; /// use multiaddr::Multiaddr;
/// ///
/// let address: Multiaddr = "/ip4/127.0.0.1/udt/sctp/5678".parse().unwrap(); /// let address: Multiaddr = "/ip4/127.0.0.1/udt/sctp/5678".parse().unwrap();
/// ///
/// assert_eq!(address.pop().unwrap(), AddrComponent::SCTP(5678)); /// assert_eq!(address.pop().unwrap(), AddrComponent::SCTP(5678));
/// assert_eq!(address.pop().unwrap(), AddrComponent::UDT); /// assert_eq!(address.pop().unwrap(), AddrComponent::UDT);
/// ``` /// ```
@ -241,7 +245,8 @@ impl Multiaddr {
impl From<AddrComponent> for Multiaddr { impl From<AddrComponent> for Multiaddr {
fn from(addr: AddrComponent) -> Multiaddr { fn from(addr: AddrComponent) -> Multiaddr {
let mut out = Vec::new(); let mut out = Vec::new();
addr.write_bytes(&mut out).expect("writing to a Vec never fails"); addr.write_bytes(&mut out)
.expect("writing to a Vec never fails");
Multiaddr { bytes: out } Multiaddr { bytes: out }
} }
} }
@ -258,11 +263,13 @@ impl<'a> IntoIterator for &'a Multiaddr {
impl FromIterator<AddrComponent> for Multiaddr { impl FromIterator<AddrComponent> for Multiaddr {
fn from_iter<T>(iter: T) -> Self fn from_iter<T>(iter: T) -> Self
where T: IntoIterator<Item = AddrComponent> where
T: IntoIterator<Item = AddrComponent>,
{ {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
for cmp in iter { for cmp in iter {
cmp.write_bytes(&mut bytes).expect("writing to a Vec never fails"); cmp.write_bytes(&mut bytes)
.expect("writing to a Vec never fails");
} }
Multiaddr { bytes: bytes } Multiaddr { bytes: bytes }
} }
@ -285,15 +292,17 @@ impl FromStr for Multiaddr {
let protocol: ProtocolId = part.parse()?; let protocol: ProtocolId = part.parse()?;
let addr_component = match protocol.size() { let addr_component = match protocol.size() {
ProtocolArgSize::Fixed { bytes: 0 } => { ProtocolArgSize::Fixed { bytes: 0 } => {
protocol.parse_data("")? // TODO: bad design protocol.parse_data("")? // TODO: bad design
}, }
_ => { _ => {
let data = parts.next().ok_or(Error::MissingAddress)?; let data = parts.next().ok_or(Error::MissingAddress)?;
protocol.parse_data(data)? protocol.parse_data(data)?
}, }
}; };
addr_component.write_bytes(&mut bytes).expect("writing to a Vec never fails"); addr_component
.write_bytes(&mut bytes)
.expect("writing to a Vec never fails");
} }
Ok(Multiaddr { bytes: bytes }) Ok(Multiaddr { bytes: bytes })
@ -311,8 +320,8 @@ impl<'a> Iterator for Iter<'a> {
return None; return None;
} }
let (component, next_data) = AddrComponent::from_bytes(self.0) let (component, next_data) =
.expect("multiaddr is known to be valid"); AddrComponent::from_bytes(self.0).expect("multiaddr is known to be valid");
self.0 = next_data; self.0 = next_data;
Some(component) Some(component)
} }

View File

@ -1,12 +1,12 @@
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr; use std::str::FromStr;
use std::convert::From; use std::convert::From;
use std::io::{Cursor, Write, Result as IoResult}; use std::io::{Cursor, Result as IoResult, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use cid::Cid; use cid::Cid;
use integer_encoding::{VarInt, VarIntWriter}; use integer_encoding::{VarInt, VarIntWriter};
use {Result, Error}; use {Error, Result};
///! # ProtocolId ///! # ProtocolId
///! ///!
@ -117,7 +117,6 @@ impl FromStr for ProtocolId {
} }
} }
impl ProtocolId { impl ProtocolId {
/// Convert a `u64` based code to a `ProtocolId`. /// Convert a `u64` based code to a `ProtocolId`.
/// ///
@ -232,12 +231,8 @@ impl ProtocolId {
let addr = Ipv6Addr::from_str(a)?; let addr = Ipv6Addr::from_str(a)?;
Ok(AddrComponent::IP6(addr)) Ok(AddrComponent::IP6(addr))
} }
ProtocolId::DNS4 => { ProtocolId::DNS4 => Ok(AddrComponent::DNS4(a.to_owned())),
Ok(AddrComponent::DNS4(a.to_owned())) ProtocolId::DNS6 => Ok(AddrComponent::DNS6(a.to_owned())),
}
ProtocolId::DNS6 => {
Ok(AddrComponent::DNS6(a.to_owned()))
}
ProtocolId::TCP => { ProtocolId::TCP => {
let parsed: u16 = a.parse()?; let parsed: u16 = a.parse()?;
Ok(AddrComponent::TCP(parsed)) Ok(AddrComponent::TCP(parsed))
@ -262,12 +257,10 @@ impl ProtocolId {
let bytes = Cid::from(a)?.to_bytes(); let bytes = Cid::from(a)?.to_bytes();
Ok(AddrComponent::IPFS(bytes)) Ok(AddrComponent::IPFS(bytes))
} }
ProtocolId::ONION => unimplemented!(), // TODO: ProtocolId::ONION => unimplemented!(), // TODO:
ProtocolId::QUIC => Ok(AddrComponent::QUIC), ProtocolId::QUIC => Ok(AddrComponent::QUIC),
ProtocolId::UTP => Ok(AddrComponent::UTP), ProtocolId::UTP => Ok(AddrComponent::UTP),
ProtocolId::UNIX => { ProtocolId::UNIX => Ok(AddrComponent::UNIX(a.to_owned())),
Ok(AddrComponent::UNIX(a.to_owned()))
}
ProtocolId::UDT => Ok(AddrComponent::UDT), ProtocolId::UDT => Ok(AddrComponent::UDT),
ProtocolId::HTTP => Ok(AddrComponent::HTTP), ProtocolId::HTTP => Ok(AddrComponent::HTTP),
ProtocolId::HTTPS => Ok(AddrComponent::HTTPS), ProtocolId::HTTPS => Ok(AddrComponent::HTTPS),
@ -342,17 +335,15 @@ impl AddrComponent {
/// Builds an `AddrComponent` from an array that starts with a bytes representation. On /// Builds an `AddrComponent` from an array that starts with a bytes representation. On
/// success, also returns the rest of the slice. /// success, also returns the rest of the slice.
pub fn from_bytes(input: &[u8]) -> Result<(AddrComponent, &[u8])> { pub fn from_bytes(input: &[u8]) -> Result<(AddrComponent, &[u8])> {
let (proto_num, proto_id_len) = u64::decode_var(input); // TODO: will panic if ID too large let (proto_num, proto_id_len) = u64::decode_var(input); // TODO: will panic if ID too large
let protocol_id = ProtocolId::from(proto_num)?; let protocol_id = ProtocolId::from(proto_num)?;
let (data_offset, data_size) = match protocol_id.size() { let (data_offset, data_size) = match protocol_id.size() {
ProtocolArgSize::Fixed { bytes } => { ProtocolArgSize::Fixed { bytes } => (0, bytes),
(0, bytes)
},
ProtocolArgSize::Variable => { ProtocolArgSize::Variable => {
let (data_size, varint_len) = u64::decode_var(&input[proto_id_len..]); // TODO: will panic if ID too large let (data_size, varint_len) = u64::decode_var(&input[proto_id_len..]); // TODO: will panic if ID too large
(varint_len, data_size as usize) (varint_len, data_size as usize)
}, }
}; };
let (data, rest) = input[proto_id_len..][data_offset..].split_at(data_size); let (data, rest) = input[proto_id_len..][data_offset..].split_at(data_size);
@ -360,7 +351,7 @@ impl AddrComponent {
let addr_component = match protocol_id { let addr_component = match protocol_id {
ProtocolId::IP4 => { ProtocolId::IP4 => {
AddrComponent::IP4(Ipv4Addr::new(data[0], data[1], data[2], data[3])) AddrComponent::IP4(Ipv4Addr::new(data[0], data[1], data[2], data[3]))
}, }
ProtocolId::IP6 => { ProtocolId::IP6 => {
let mut rdr = Cursor::new(data); let mut rdr = Cursor::new(data);
let mut seg = vec![]; let mut seg = vec![];
@ -369,22 +360,20 @@ impl AddrComponent {
seg.push(rdr.read_u16::<BigEndian>()?); seg.push(rdr.read_u16::<BigEndian>()?);
} }
let addr = Ipv6Addr::new(seg[0], let addr = Ipv6Addr::new(
seg[1], seg[0],
seg[2], seg[1],
seg[3], seg[2],
seg[4], seg[3],
seg[5], seg[4],
seg[6], seg[5],
seg[7]); seg[6],
seg[7],
);
AddrComponent::IP6(addr) AddrComponent::IP6(addr)
} }
ProtocolId::DNS4 => { ProtocolId::DNS4 => AddrComponent::DNS4(String::from_utf8(data.to_owned())?),
AddrComponent::DNS4(String::from_utf8(data.to_owned())?) ProtocolId::DNS6 => AddrComponent::DNS6(String::from_utf8(data.to_owned())?),
}
ProtocolId::DNS6 => {
AddrComponent::DNS6(String::from_utf8(data.to_owned())?)
}
ProtocolId::TCP => { ProtocolId::TCP => {
let mut rdr = Cursor::new(data); let mut rdr = Cursor::new(data);
let num = rdr.read_u16::<BigEndian>()?; let num = rdr.read_u16::<BigEndian>()?;
@ -405,9 +394,7 @@ impl AddrComponent {
let num = rdr.read_u16::<BigEndian>()?; let num = rdr.read_u16::<BigEndian>()?;
AddrComponent::SCTP(num) AddrComponent::SCTP(num)
} }
ProtocolId::UNIX => { ProtocolId::UNIX => AddrComponent::UNIX(String::from_utf8(data.to_owned())?),
AddrComponent::UNIX(String::from_utf8(data.to_owned())?)
}
ProtocolId::P2P => { ProtocolId::P2P => {
let bytes = Cid::from(data)?.to_bytes(); let bytes = Cid::from(data)?.to_bytes();
AddrComponent::P2P(bytes) AddrComponent::P2P(bytes)
@ -416,7 +403,7 @@ impl AddrComponent {
let bytes = Cid::from(data)?.to_bytes(); let bytes = Cid::from(data)?.to_bytes();
AddrComponent::IPFS(bytes) AddrComponent::IPFS(bytes)
} }
ProtocolId::ONION => unimplemented!(), // TODO: ProtocolId::ONION => unimplemented!(), // TODO:
ProtocolId::QUIC => AddrComponent::QUIC, ProtocolId::QUIC => AddrComponent::QUIC,
ProtocolId::UTP => AddrComponent::UTP, ProtocolId::UTP => AddrComponent::UTP,
ProtocolId::UDT => AddrComponent::UDT, ProtocolId::UDT => AddrComponent::UDT,
@ -441,13 +428,13 @@ impl AddrComponent {
AddrComponent::IP4(addr) => { AddrComponent::IP4(addr) => {
out.write_all(&addr.octets())?; out.write_all(&addr.octets())?;
} }
AddrComponent::IP6(addr) => { AddrComponent::IP6(addr) => for &segment in &addr.segments() {
for &segment in &addr.segments() { out.write_u16::<BigEndian>(segment)?;
out.write_u16::<BigEndian>(segment)?; },
} AddrComponent::TCP(port)
} | AddrComponent::UDP(port)
AddrComponent::TCP(port) | AddrComponent::UDP(port) | AddrComponent::DCCP(port) | | AddrComponent::DCCP(port)
AddrComponent::SCTP(port) => { | AddrComponent::SCTP(port) => {
out.write_u16::<BigEndian>(port)?; out.write_u16::<BigEndian>(port)?;
} }
AddrComponent::DNS4(s) | AddrComponent::DNS6(s) | AddrComponent::UNIX(s) => { AddrComponent::DNS4(s) | AddrComponent::DNS6(s) | AddrComponent::UNIX(s) => {
@ -460,19 +447,19 @@ impl AddrComponent {
out.write_all(&bytes)?; out.write_all(&bytes)?;
} }
AddrComponent::ONION(_) => { AddrComponent::ONION(_) => {
unimplemented!() // TODO: unimplemented!() // TODO:
}, }
AddrComponent::QUIC | AddrComponent::QUIC
AddrComponent::UTP | | AddrComponent::UTP
AddrComponent::UDT | | AddrComponent::UDT
AddrComponent::HTTP | | AddrComponent::HTTP
AddrComponent::HTTPS | | AddrComponent::HTTPS
AddrComponent::WS | | AddrComponent::WS
AddrComponent::WSS | | AddrComponent::WSS
AddrComponent::Libp2pWebsocketStar | | AddrComponent::Libp2pWebsocketStar
AddrComponent::Libp2pWebrtcStar | | AddrComponent::Libp2pWebrtcStar
AddrComponent::Libp2pWebrtcDirect | | AddrComponent::Libp2pWebrtcDirect
AddrComponent::P2pCircuit => {} | AddrComponent::P2pCircuit => {}
}; };
Ok(()) Ok(())
@ -497,15 +484,15 @@ impl ToString for AddrComponent {
// TODO: meh for cloning // TODO: meh for cloning
let c = Cid::from(bytes.clone()).expect("cid is known to be valid"); let c = Cid::from(bytes.clone()).expect("cid is known to be valid");
format!("/p2p/{}", c) format!("/p2p/{}", c)
}, }
AddrComponent::IPFS(ref bytes) => { AddrComponent::IPFS(ref bytes) => {
// TODO: meh for cloning // TODO: meh for cloning
let c = Cid::from(bytes.clone()).expect("cid is known to be valid"); let c = Cid::from(bytes.clone()).expect("cid is known to be valid");
format!("/ipfs/{}", c) format!("/ipfs/{}", c)
}, }
AddrComponent::HTTP => format!("/http"), AddrComponent::HTTP => format!("/http"),
AddrComponent::HTTPS => format!("/https"), AddrComponent::HTTPS => format!("/https"),
AddrComponent::ONION(_) => unimplemented!(),//format!("/onion"), // TODO: AddrComponent::ONION(_) => unimplemented!(), //format!("/onion"), // TODO:
AddrComponent::QUIC => format!("/quic"), AddrComponent::QUIC => format!("/quic"),
AddrComponent::WS => format!("/ws"), AddrComponent::WS => format!("/ws"),
AddrComponent::WSS => format!("/wss"), AddrComponent::WSS => format!("/wss"),

View File

@ -1,9 +1,9 @@
extern crate multiaddr;
extern crate data_encoding; extern crate data_encoding;
extern crate multiaddr;
use data_encoding::hex; use data_encoding::hex;
use multiaddr::*; use multiaddr::*;
use std::net::{SocketAddrV4, SocketAddrV6, Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
#[test] #[test]
fn protocol_to_code() { fn protocol_to_code() {
@ -15,11 +15,16 @@ fn protocol_to_name() {
assert_eq!(ProtocolId::TCP.to_string(), "tcp"); assert_eq!(ProtocolId::TCP.to_string(), "tcp");
} }
fn assert_bytes(source: &str, target: &str, protocols: Vec<ProtocolId>) -> () { fn assert_bytes(source: &str, target: &str, protocols: Vec<ProtocolId>) -> () {
let address = source.parse::<Multiaddr>().unwrap(); let address = source.parse::<Multiaddr>().unwrap();
assert_eq!(hex::encode(address.to_bytes().as_slice()), target); assert_eq!(hex::encode(address.to_bytes().as_slice()), target);
assert_eq!(address.iter().map(|addr| addr.protocol_id()).collect::<Vec<_>>(), protocols); assert_eq!(
address
.iter()
.map(|addr| addr.protocol_id())
.collect::<Vec<_>>(),
protocols
);
} }
fn ma_valid(source: &str, target: &str, protocols: Vec<ProtocolId>) -> () { fn ma_valid(source: &str, target: &str, protocols: Vec<ProtocolId>) -> () {
assert_bytes(source, target, protocols); assert_bytes(source, target, protocols);
@ -45,9 +50,11 @@ fn construct_success() {
ma_valid("/ip4/1.2.3.4", "0401020304", vec![IP4]); ma_valid("/ip4/1.2.3.4", "0401020304", vec![IP4]);
ma_valid("/ip4/0.0.0.0", "0400000000", vec![IP4]); ma_valid("/ip4/0.0.0.0", "0400000000", vec![IP4]);
ma_valid("/ip6/::1", "2900000000000000000000000000000001", vec![IP6]); ma_valid("/ip6/::1", "2900000000000000000000000000000001", vec![IP6]);
ma_valid("/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21", ma_valid(
"29260100094F819700803ECA6566E80C21", "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21",
vec![IP6]); "29260100094F819700803ECA6566E80C21",
vec![IP6],
);
ma_valid("/udp/0", "110000", vec![UDP]); ma_valid("/udp/0", "110000", vec![UDP]);
ma_valid("/tcp/0", "060000", vec![TCP]); ma_valid("/tcp/0", "060000", vec![TCP]);
ma_valid("/sctp/0", "84010000", vec![SCTP]); ma_valid("/sctp/0", "84010000", vec![SCTP]);
@ -56,116 +63,156 @@ fn construct_success() {
ma_valid("/sctp/1234", "840104D2", vec![SCTP]); ma_valid("/sctp/1234", "840104D2", vec![SCTP]);
ma_valid("/udp/65535", "11FFFF", vec![UDP]); ma_valid("/udp/65535", "11FFFF", vec![UDP]);
ma_valid("/tcp/65535", "06FFFF", vec![TCP]); ma_valid("/tcp/65535", "06FFFF", vec![TCP]);
ma_valid("/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", ma_valid(
"A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", "/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
vec![IPFS]); "A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
vec![IPFS],
);
ma_valid("/udp/1234/sctp/1234", "1104D2840104D2", vec![UDP, SCTP]); ma_valid("/udp/1234/sctp/1234", "1104D2840104D2", vec![UDP, SCTP]);
ma_valid("/udp/1234/udt", "1104D2AD02", vec![UDP, UDT]); ma_valid("/udp/1234/udt", "1104D2AD02", vec![UDP, UDT]);
ma_valid("/udp/1234/utp", "1104D2AE02", vec![UDP, UTP]); ma_valid("/udp/1234/utp", "1104D2AE02", vec![UDP, UTP]);
ma_valid("/tcp/1234/http", "0604D2E003", vec![TCP, HTTP]); ma_valid("/tcp/1234/http", "0604D2E003", vec![TCP, HTTP]);
ma_valid("/tcp/1234/https", "0604D2BB03", vec![TCP, HTTPS]); ma_valid("/tcp/1234/https", "0604D2BB03", vec![TCP, HTTPS]);
ma_valid("/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", ma_valid(
"A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2", "/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
vec![IPFS, TCP]); "A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2",
ma_valid("/ip4/127.0.0.1/udp/1234", vec![IPFS, TCP],
"047F0000011104D2", );
vec![IP4, UDP]); ma_valid(
"/ip4/127.0.0.1/udp/1234",
"047F0000011104D2",
vec![IP4, UDP],
);
ma_valid("/ip4/127.0.0.1/udp/0", "047F000001110000", vec![IP4, UDP]); ma_valid("/ip4/127.0.0.1/udp/0", "047F000001110000", vec![IP4, UDP]);
ma_valid("/ip4/127.0.0.1/tcp/1234", ma_valid(
"047F0000010604D2", "/ip4/127.0.0.1/tcp/1234",
vec![IP4, TCP]); "047F0000010604D2",
ma_valid("/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", vec![IP4, TCP],
"047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", );
vec![IP4, IPFS]); ma_valid(
ma_valid("/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", "/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
"047F000001A503221220D52EBB89D85B02A284948203A\ "047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2", vec![IP4, IPFS],
vec![IP4, IPFS, TCP]); );
ma_valid(
"/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
"047F000001A503221220D52EBB89D85B02A284948203A\
62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2",
vec![IP4, IPFS, TCP],
);
// /unix/a/b/c/d/e, // /unix/a/b/c/d/e,
// /unix/stdio, // /unix/stdio,
// /ip4/1.2.3.4/tcp/80/unix/a/b/c/d/e/f, // /ip4/1.2.3.4/tcp/80/unix/a/b/c/d/e/f,
// /ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234/unix/stdio // /ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234/unix/stdio
ma_valid("/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:\ ma_valid(
7095/tcp/8000/ws/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:\
"29200108A07AC542013AC986FFFE317095061F40DD03A5\ 7095/tcp/8000/ws/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
03221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", "29200108A07AC542013AC986FFFE317095061F40DD03A5\
vec![IP6, TCP, WS, IPFS]); 03221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
ma_valid("/p2p-webrtc-star/ip4/127.0.0.\ vec![IP6, TCP, WS, IPFS],
1/tcp/9090/ws/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", );
"9302047F000001062382DD03A503221220D52EBB89D85B\ ma_valid(
02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", "/p2p-webrtc-star/ip4/127.0.0.\
vec![Libp2pWebrtcStar, IP4, TCP, WS, IPFS]); 1/tcp/9090/ws/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
ma_valid("/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:\ "9302047F000001062382DD03A503221220D52EBB89D85B\
7095/tcp/8000/wss/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", 02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
"29200108A07AC542013AC986FFFE317095061F40DE03A503221220D52EBB8\ vec![Libp2pWebrtcStar, IP4, TCP, WS, IPFS],
9D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", );
vec![IP6, TCP, WSS, IPFS]); ma_valid(
ma_valid("/ip4/127.0.0.1/tcp/9090/p2p-circuit/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:\
"047F000001062382A202A503221220D52EBB89D85B\ 7095/tcp/8000/wss/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", "29200108A07AC542013AC986FFFE317095061F40DE03A503221220D52EBB8\
vec![IP4, TCP, P2pCircuit, IPFS]); 9D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
vec![IP6, TCP, WSS, IPFS],
);
ma_valid(
"/ip4/127.0.0.1/tcp/9090/p2p-circuit/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
"047F000001062382A202A503221220D52EBB89D85B\
02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
vec![IP4, TCP, P2pCircuit, IPFS],
);
} }
#[test] #[test]
fn construct_fail() { fn construct_fail() {
let addresses = ["/ip4", let addresses = [
"/ip4/::1", "/ip4",
"/ip4/fdpsofodsajfdoisa", "/ip4/::1",
"/ip6", "/ip4/fdpsofodsajfdoisa",
"/udp", "/ip6",
"/tcp", "/udp",
"/sctp", "/tcp",
"/udp/65536", "/sctp",
"/tcp/65536", "/udp/65536",
// "/onion/9imaq4ygg2iegci7:80", "/tcp/65536",
// "/onion/aaimaq4ygg2iegci7:80", // "/onion/9imaq4ygg2iegci7:80",
// "/onion/timaq4ygg2iegci7:0", // "/onion/aaimaq4ygg2iegci7:80",
// "/onion/timaq4ygg2iegci7:-1", // "/onion/timaq4ygg2iegci7:0",
// "/onion/timaq4ygg2iegci7", // "/onion/timaq4ygg2iegci7:-1",
// "/onion/timaq4ygg2iegci@:666", // "/onion/timaq4ygg2iegci7",
"/udp/1234/sctp", // "/onion/timaq4ygg2iegci@:666",
"/udp/1234/udt/1234", "/udp/1234/sctp",
"/udp/1234/utp/1234", "/udp/1234/udt/1234",
"/ip4/127.0.0.1/udp/jfodsajfidosajfoidsa", "/udp/1234/utp/1234",
"/ip4/127.0.0.1/udp", "/ip4/127.0.0.1/udp/jfodsajfidosajfoidsa",
"/ip4/127.0.0.1/tcp/jfodsajfidosajfoidsa", "/ip4/127.0.0.1/udp",
"/ip4/127.0.0.1/tcp", "/ip4/127.0.0.1/tcp/jfodsajfidosajfoidsa",
"/ip4/127.0.0.1/ipfs", "/ip4/127.0.0.1/tcp",
"/ip4/127.0.0.1/ipfs/tcp", "/ip4/127.0.0.1/ipfs",
"/p2p-circuit/50"]; "/ip4/127.0.0.1/ipfs/tcp",
"/p2p-circuit/50",
];
for address in &addresses { for address in &addresses {
assert!(address.parse::<Multiaddr>().is_err(), address.to_string()); assert!(address.parse::<Multiaddr>().is_err(), address.to_string());
} }
} }
#[test] #[test]
fn to_multiaddr() { fn to_multiaddr() {
assert_eq!(Ipv4Addr::new(127, 0, 0, 1).to_multiaddr().unwrap(), assert_eq!(
"/ip4/127.0.0.1".parse::<Multiaddr>().unwrap()); Ipv4Addr::new(127, 0, 0, 1).to_multiaddr().unwrap(),
assert_eq!(Ipv6Addr::new(0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21) "/ip4/127.0.0.1".parse::<Multiaddr>().unwrap()
.to_multiaddr() );
.unwrap(), assert_eq!(
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21".parse::<Multiaddr>().unwrap()); Ipv6Addr::new(0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21)
assert_eq!("/ip4/127.0.0.1/tcp/1234".to_string().to_multiaddr().unwrap(), .to_multiaddr()
"/ip4/127.0.0.1/tcp/1234".parse::<Multiaddr>().unwrap()); .unwrap(),
assert_eq!("/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21".to_multiaddr().unwrap(), "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21"
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21".parse::<Multiaddr>().unwrap()); .parse::<Multiaddr>()
assert_eq!(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1234).to_multiaddr().unwrap(), .unwrap()
"/ip4/127.0.0.1/tcp/1234".parse::<Multiaddr>().unwrap()); );
assert_eq!(SocketAddrV6::new(Ipv6Addr::new(0x2601, assert_eq!(
0x9, "/ip4/127.0.0.1/tcp/1234"
0x4f81, .to_string()
0x9700, .to_multiaddr()
0x803e, .unwrap(),
0xca65, "/ip4/127.0.0.1/tcp/1234".parse::<Multiaddr>().unwrap()
0x66e8, );
0xc21), assert_eq!(
1234, "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21"
0, .to_multiaddr()
0) .unwrap(),
.to_multiaddr() "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21"
.unwrap(), .parse::<Multiaddr>()
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/tcp/1234".parse::<Multiaddr>().unwrap()); .unwrap()
);
assert_eq!(
SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1234)
.to_multiaddr()
.unwrap(),
"/ip4/127.0.0.1/tcp/1234".parse::<Multiaddr>().unwrap()
);
assert_eq!(
SocketAddrV6::new(
Ipv6Addr::new(0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21),
1234,
0,
0
).to_multiaddr()
.unwrap(),
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/tcp/1234"
.parse::<Multiaddr>()
.unwrap()
);
} }

View File

@ -1,17 +0,0 @@
verbose=false
max_width=100
comment_width=100
tab_spaces=4
fn_call_width=100
struct_lit_width=32
fn_call_style="Visual"
single_line_if_else_max_width=100
trailing_comma="Vertical"
chain_indent="Visual"
chain_one_line_max=100
reorder_imports=true
format_strings=false
hard_tabs=true
wrap_match_arms=false
error_on_line_overflow=false
where_style="Legacy"

View File

@ -1,21 +1,21 @@
// Copyright 2017 Parity Technologies (UK) Ltd. // Copyright 2017 Parity Technologies (UK) Ltd.
// //
// Permission is hereby granted, free of charge, to any person obtaining a // Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"), // copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation // to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, // the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the // and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions: // Software is furnished to do so, subject to the following conditions:
// //
// The above copyright notice and this permission notice shall be included in // The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
// TODO: use this once stable ; for now we just copy-paste the content of the README.md // TODO: use this once stable ; for now we just copy-paste the content of the README.md
@ -39,16 +39,24 @@ use std::io::Error as IoError;
use std::io::ErrorKind as IoErrorKind; use std::io::ErrorKind as IoErrorKind;
use std::io::{Read, Write}; use std::io::{Read, Write};
use bytes::{Buf, IntoBuf}; use bytes::{Buf, IntoBuf};
use futures::{Async, AsyncSink, Poll, Stream, Sink}; use futures::{Async, AsyncSink, Poll, Sink, Stream};
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
/// Wraps around a `Stream + Sink` whose items are buffers. Implements `AsyncRead` and `AsyncWrite`. /// Wraps around a `Stream + Sink` whose items are buffers. Implements `AsyncRead` and `AsyncWrite`.
pub struct RwStreamSink<S> where S: Stream, S::Item: IntoBuf { pub struct RwStreamSink<S>
where
S: Stream,
S::Item: IntoBuf,
{
inner: S, inner: S,
current_item: Option<<S::Item as IntoBuf>::Buf>, current_item: Option<<S::Item as IntoBuf>::Buf>,
} }
impl<S> RwStreamSink<S> where S: Stream, S::Item: IntoBuf { impl<S> RwStreamSink<S>
where
S: Stream,
S::Item: IntoBuf,
{
/// Wraps around `inner`. /// Wraps around `inner`.
#[inline] #[inline]
pub fn new(inner: S) -> RwStreamSink<S> { pub fn new(inner: S) -> RwStreamSink<S> {
@ -60,8 +68,9 @@ impl<S> RwStreamSink<S> where S: Stream, S::Item: IntoBuf {
} }
impl<S> Read for RwStreamSink<S> impl<S> Read for RwStreamSink<S>
where S: Stream<Error = IoError>, where
S::Item: IntoBuf, S: Stream<Error = IoError>,
S::Item: IntoBuf,
{ {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> { fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
let mut written = 0; let mut written = 0;
@ -82,14 +91,14 @@ impl<S> Read for RwStreamSink<S>
} else { } else {
return Ok(written); return Ok(written);
} }
}, }
Err(err) => { Err(err) => {
if written == 0 { if written == 0 {
return Err(err); return Err(err);
} else { } else {
return Ok(written); return Ok(written);
} }
}, }
}; };
} }
@ -103,22 +112,27 @@ impl<S> Read for RwStreamSink<S>
return Ok(written); return Ok(written);
} }
current_item.by_ref().take(to_copy).copy_to_slice(&mut buf[written..(written+to_copy)]); current_item
.by_ref()
.take(to_copy)
.copy_to_slice(&mut buf[written..(written + to_copy)]);
written += to_copy; written += to_copy;
} }
} }
} }
impl<S> AsyncRead for RwStreamSink<S> impl<S> AsyncRead for RwStreamSink<S>
where S: Stream<Error = IoError>, where
S::Item: IntoBuf S: Stream<Error = IoError>,
S::Item: IntoBuf,
{ {
} }
impl<S> Write for RwStreamSink<S> impl<S> Write for RwStreamSink<S>
where S: Stream + Sink<SinkError = IoError>, where
S::SinkItem: for<'r> From<&'r [u8]>, S: Stream + Sink<SinkError = IoError>,
S::Item: IntoBuf S::SinkItem: for<'r> From<&'r [u8]>,
S::Item: IntoBuf,
{ {
#[inline] #[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> { fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
@ -137,9 +151,10 @@ impl<S> Write for RwStreamSink<S>
} }
impl<S> AsyncWrite for RwStreamSink<S> impl<S> AsyncWrite for RwStreamSink<S>
where S: Stream + Sink<SinkError = IoError>, where
S::SinkItem: for<'r> From<&'r [u8]>, S: Stream + Sink<SinkError = IoError>,
S::Item: IntoBuf S::SinkItem: for<'r> From<&'r [u8]>,
S::Item: IntoBuf,
{ {
#[inline] #[inline]
fn shutdown(&mut self) -> Poll<(), IoError> { fn shutdown(&mut self) -> Poll<(), IoError> {
@ -150,24 +165,33 @@ impl<S> AsyncWrite for RwStreamSink<S>
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bytes::Bytes; use bytes::Bytes;
use futures::{Sink, Stream, Future, Poll, StartSend}; use futures::{Future, Poll, Sink, StartSend, Stream};
use futures::sync::mpsc::channel; use futures::sync::mpsc::channel;
use std::io::Read; use std::io::Read;
use RwStreamSink; use RwStreamSink;
// This struct merges a stream and a sink and is quite useful for tests. // This struct merges a stream and a sink and is quite useful for tests.
struct Wrapper<St, Si>(St, Si); struct Wrapper<St, Si>(St, Si);
impl<St, Si> Stream for Wrapper<St, Si> where St: Stream { impl<St, Si> Stream for Wrapper<St, Si>
where
St: Stream,
{
type Item = St::Item; type Item = St::Item;
type Error = St::Error; type Error = St::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
self.0.poll() self.0.poll()
} }
} }
impl<St, Si> Sink for Wrapper<St, Si> where Si: Sink { impl<St, Si> Sink for Wrapper<St, Si>
where
Si: Sink,
{
type SinkItem = Si::SinkItem; type SinkItem = Si::SinkItem;
type SinkError = Si::SinkError; type SinkError = Si::SinkError;
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> { fn start_send(
&mut self,
item: Self::SinkItem,
) -> StartSend<Self::SinkItem, Self::SinkError> {
self.1.start_send(item) self.1.start_send(item)
} }
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
@ -185,7 +209,8 @@ mod tests {
tx2.send(Bytes::from("hel")) tx2.send(Bytes::from("hel"))
.and_then(|tx| tx.send(Bytes::from("lo wor"))) .and_then(|tx| tx.send(Bytes::from("lo wor")))
.and_then(|tx| tx.send(Bytes::from("ld"))) .and_then(|tx| tx.send(Bytes::from("ld")))
.wait().unwrap(); .wait()
.unwrap();
let mut data1 = [0u8; 5]; let mut data1 = [0u8; 5];
assert_eq!(wrapper.read(&mut data1).unwrap(), 5); assert_eq!(wrapper.read(&mut data1).unwrap(), 5);

View File

@ -23,20 +23,20 @@
//! Encoding and decoding state machines for protobuf varints //! Encoding and decoding state machines for protobuf varints
// TODO: Non-allocating `BigUint`? // TODO: Non-allocating `BigUint`?
extern crate bytes;
#[macro_use]
extern crate error_chain;
extern crate futures;
extern crate num_bigint; extern crate num_bigint;
extern crate num_traits; extern crate num_traits;
extern crate tokio_io; extern crate tokio_io;
extern crate bytes;
extern crate futures;
#[macro_use]
extern crate error_chain;
use bytes::{BufMut, Bytes, BytesMut, IntoBuf}; use bytes::{BufMut, Bytes, BytesMut, IntoBuf};
use futures::{Poll, Async}; use futures::{Async, Poll};
use num_bigint::BigUint; use num_bigint::BigUint;
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use tokio_io::codec::{Encoder, Decoder}; use tokio_io::codec::{Decoder, Encoder};
use std::io; use std::io;
use std::io::prelude::*; use std::io::prelude::*;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -143,7 +143,10 @@ macro_rules! impl_decoderstate {
($t:ty, $make_fn:expr, $shift_fn:expr) => { ($t:ty, $make_fn:expr, $shift_fn:expr) => {
impl DecoderHelper for $t { impl DecoderHelper for $t {
#[inline] #[inline]
fn decode_one(decoder: &mut DecoderState<Self>, byte: u8) -> ::errors::Result<Option<$t>> { fn decode_one(
decoder: &mut DecoderState<Self>,
byte: u8,
) -> ::errors::Result<Option<$t>> {
let res = decoder.accumulator.take().and_then(|accumulator| { let res = decoder.accumulator.take().and_then(|accumulator| {
let out = accumulator | match $shift_fn( let out = accumulator | match $shift_fn(
$make_fn(byte & 0x7F), $make_fn(byte & 0x7F),
@ -439,16 +442,14 @@ impl<T> Decoder for VarintCodec<T> {
self.inner = VarintCodecInner::WaitingForData(len); self.inner = VarintCodecInner::WaitingForData(len);
return Ok(None); return Ok(None);
} }
}, }
VarintCodecInner::WaitingForLen(mut decoder) => { VarintCodecInner::WaitingForLen(mut decoder) => match decoder.decode(src)? {
match decoder.decode(src)? { None => {
None => { self.inner = VarintCodecInner::WaitingForLen(decoder);
self.inner = VarintCodecInner::WaitingForLen(decoder); return Ok(None);
return Ok(None); }
}, Some(len) => {
Some(len) => { self.inner = VarintCodecInner::WaitingForData(len);
self.inner = VarintCodecInner::WaitingForData(len);
},
} }
}, },
VarintCodecInner::Poisoned => panic!("varint codec was poisoned"), VarintCodecInner::Poisoned => panic!("varint codec was poisoned"),
@ -458,7 +459,8 @@ impl<T> Decoder for VarintCodec<T> {
} }
impl<D> Encoder for VarintCodec<D> impl<D> Encoder for VarintCodec<D>
where D: IntoBuf + AsRef<[u8]>, where
D: IntoBuf + AsRef<[u8]>,
{ {
type Item = D; type Item = D;
type Error = io::Error; type Error = io::Error;
@ -507,7 +509,7 @@ pub fn encode<T: EncoderHelper + Bits>(input: T) -> Bytes {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{decode, VarintDecoder, EncoderState}; use super::{decode, EncoderState, VarintDecoder};
use tokio_io::codec::FramedRead; use tokio_io::codec::FramedRead;
use num_bigint::BigUint; use num_bigint::BigUint;
use futures::{Future, Stream}; use futures::{Future, Stream};