mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-22 06:11:34 +00:00
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:
@ -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
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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| {
|
||||||
|
@ -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 = _>>
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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!());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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!());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
@ -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();
|
||||||
|
@ -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()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -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()))
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
@ -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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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"),
|
||||||
|
@ -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()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
17
rustfmt.toml
17
rustfmt.toml
@ -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"
|
|
@ -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);
|
||||||
|
@ -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};
|
||||||
|
Reference in New Issue
Block a user