diff --git a/Cargo.lock b/Cargo.lock
index fdcb2707b948b3aff4a9c16de31eeba7c39cb7c5..8671f8c1da6e24ee007f6a0567839a0dd3281d2e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -58,23 +58,6 @@ name = "base58"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "base64"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "base64"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "base64"
 version = "0.9.1"
@@ -152,23 +135,6 @@ dependencies = [
  "yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "core-foundation"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "core-foundation-sys"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "crossbeam-deque"
 version = "0.2.0"
@@ -178,15 +144,6 @@ dependencies = [
  "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "crossbeam-deque"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "crossbeam-epoch"
 version = "0.3.1"
@@ -201,19 +158,6 @@ dependencies = [
  "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "crossbeam-epoch"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "crossbeam-utils"
 version = "0.2.2"
@@ -222,14 +166,6 @@ dependencies = [
  "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "crossbeam-utils"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "dtoa"
 version = "0.4.2"
@@ -428,7 +364,7 @@ dependencies = [
  "serde_derive 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "sqlite 0.23.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "websocket 0.20.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ws 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -491,11 +427,6 @@ name = "fuchsia-zircon-sys"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "futures"
-version = "0.1.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
 [[package]]
 name = "gcc"
 version = "0.3.54"
@@ -506,24 +437,6 @@ name = "httparse"
 version = "1.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "hyper"
-version = "0.10.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "idna"
 version = "0.1.4"
@@ -557,16 +470,6 @@ dependencies = [
  "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "language-tags"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "lazy_static"
-version = "0.2.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
 [[package]]
 name = "lazy_static"
 version = "1.0.0"
@@ -587,14 +490,6 @@ name = "linked-hash-map"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "log"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "log"
 version = "0.4.1"
@@ -621,14 +516,6 @@ name = "memoffset"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "mime"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "mio"
 version = "0.6.14"
@@ -658,20 +545,6 @@ dependencies = [
  "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "native-tls"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
- "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "net2"
 version = "0.2.32"
@@ -868,14 +741,6 @@ dependencies = [
  "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "remove_dir_all"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "rust-crypto"
 version = "0.2.36"
@@ -914,45 +779,11 @@ name = "safemem"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "schannel"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "scoped-tls"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
 [[package]]
 name = "scopeguard"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "security-framework"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
- "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "security-framework-sys"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "serde"
 version = "1.0.57"
@@ -980,7 +811,7 @@ dependencies = [
 
 [[package]]
 name = "sha1"
-version = "0.2.0"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -993,6 +824,11 @@ dependencies = [
  "term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "slab"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "slab"
 version = "0.4.0"
@@ -1067,15 +903,6 @@ dependencies = [
  "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "tempdir"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "term"
 version = "0.5.1"
@@ -1130,164 +957,11 @@ dependencies = [
  "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "tokio"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-fs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "tokio-core"
-version = "0.1.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "tokio-executor"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "tokio-fs"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "tokio-io"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "tokio-reactor"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "tokio-tcp"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "tokio-threadpool"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "tokio-timer"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "tokio-tls"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "tokio-udp"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "traitobject"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "typeable"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
 [[package]]
 name = "ucd-util"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "unicase"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "unicode-bidi"
 version = "0.3.4"
@@ -1349,37 +1023,11 @@ name = "vec_map"
 version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "version_check"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
 [[package]]
 name = "void"
 version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "websocket"
-version = "0.20.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
- "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
- "sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "winapi"
 version = "0.2.8"
@@ -1409,6 +1057,23 @@ name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "ws"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytes 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "ws2_32-sys"
 version = "0.2.1"
@@ -1431,8 +1096,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum backtrace 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbdd17cd962b570302f5297aea8648d5923e22e555c2ed2d8b2e34eca646bf6d"
 "checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e"
 "checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83"
-"checksum base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557"
-"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
 "checksum base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9263aa6a38da271eec5c91a83ce1e800f093c8535788d403d626d8d5c3f8f007"
 "checksum bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bda13183df33055cbb84b847becce220d392df502ebe7a4a78d7021771ed94d0"
 "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
@@ -1443,14 +1106,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18"
 "checksum chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6"
 "checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536"
-"checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67"
-"checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d"
 "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
-"checksum crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8153ef04a7594ded05b427ffad46ddeaf22e63fd48d42b3e1e3bb4db07cae7"
 "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
-"checksum crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4e2817eb773f770dcb294127c011e22771899c21d18fce7dd739c0b9832e81"
 "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
-"checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b"
 "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
 "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
 "checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82"
@@ -1459,29 +1117,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
 "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
 "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
-"checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c"
 "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
 "checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37"
-"checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2"
 "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
 "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
 "checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
-"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
 "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
 "checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef"
 "checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b"
 "checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e"
-"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
 "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
 "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
 "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
 "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
-"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
 "checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe"
 "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
-"checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0"
 "checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0"
 "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
 "checksum num-integer 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac0ea58d64a89d9d6b7688031b3be9358d6c919badcf7fbb0527ccfd891ee45"
@@ -1505,22 +1156,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75ecf88252dce580404a22444fc7d626c01815debba56a7f4f536772a5ff19d3"
 "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
 "checksum regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1ac0f60d675cc6cf13a20ec076568254472551051ad5dd050364d70671bf6b"
-"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
 "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
 "checksum rustbreak 2.0.0-rc2 (registry+https://github.com/rust-lang/crates.io-index)" = "676cb04876f4391d5d2f2c9029d10cfe5fe28ad625ad8460531bed1191477083"
 "checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649"
 "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
 "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
-"checksum schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "85fd9df495640643ad2d00443b3d78aae69802ad488debab4f1dd52fc1806ade"
-"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
 "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
-"checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332"
-"checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead"
 "checksum serde 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)" = "9478f147957b713a156ce5e4529d77275bbcfddc29563b794939b36230df8ca8"
 "checksum serde_derive 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)" = "1bdba9c305f1aeff7e83e2ff0685a141780de943cee66bdd89b63913b2b69c86"
 "checksum serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "f3ad6d546e765177cf3dded3c2e424a8040f870083a0e64064746b958ece9cb1"
-"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
+"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
 "checksum simplelog 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9cc12b39fdf4c9a07f88bffac2d628f0118ed5ac077a4b0feece61fadf1429e5"
+"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
 "checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d"
 "checksum sqlite 0.23.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d18d7b10278336e7fd9dc259399a0f9ff419616738b6a841b136c362e16db626"
 "checksum sqlite3-src 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "46e0bc115b563b1ee6c665ef895b56bf488522f57d1c6571887547c57c8f5a88"
@@ -1530,28 +1177,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum syn 0.13.10 (registry+https://github.com/rust-lang/crates.io-index)" = "77961dcdac942fa8bc033c16f3a790b311c8a27d00811b878ebd8cf9b7ba39d5"
 "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
 "checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd"
-"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
 "checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561"
 "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
 "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
 "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
 "checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865"
 "checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b"
-"checksum tokio 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ee337e5f4e501fc32966fec6fe0ca0cc1c237b0b1b14a335f8bfe3c5f06e286"
-"checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71"
-"checksum tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8cac2a7883ff3567e9d66bb09100d09b33d90311feca0206c7ca034bc0c55113"
-"checksum tokio-fs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "76766830bbf9a2d5bfb50c95350d56a2e79e2c80f675967fff448bc615899708"
-"checksum tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af9eb326f64b2d6b68438e1953341e00ab3cf54de7e35d92bfc73af8555313a"
-"checksum tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3cedc8e5af5131dc3423ffa4f877cce78ad25259a9a62de0613735a13ebc64b"
-"checksum tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec9b094851aadd2caf83ba3ad8e8c4ce65a42104f7b94d9e6550023f0407853f"
-"checksum tokio-threadpool 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c3873a6d8d0b636e024e77b9a82eaab6739578a06189ecd0e731c7308fbc5d"
-"checksum tokio-timer 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "028b94314065b90f026a21826cffd62a4e40a92cda3e5c069cc7b02e5945f5e9"
-"checksum tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "772f4b04e560117fe3b0a53e490c16ddc8ba6ec437015d91fa385564996ed913"
-"checksum tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "137bda266504893ac4774e0ec4c2108f7ccdbcb7ac8dced6305fe9e4e0b5041a"
-"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
-"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
 "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
-"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
 "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
 "checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25"
 "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
@@ -1562,13 +1194,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
 "checksum vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ed0f6789c8a85ca41bbc1c9d175422116a9869bd1cf31bb08e1493ecce60380"
 "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
-"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
 "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
-"checksum websocket 0.20.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eb277e7f4c23dc49176f74ae200e77651764efb2c25f56ad2d22623b63826369"
 "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
 "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
 "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
 "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+"checksum ws 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ccf752fee5350ca505fdb0b34d503b17d1528bd867561b7aa91d6ea750d5e972"
 "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
 "checksum yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992"
diff --git a/Cargo.toml b/Cargo.toml
index d718f4841291111a7ce8bbedf80889222c7c0a59..3eecdcaed45e67aa65b3fbca4941d536e73be393 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,16 +7,16 @@ license = "AGPL-3.0"
 
 [dependencies]
 duniter-core = { path = "./core" }
-duniter-tui = { path = "./tui" }
-duniter-ws2p = { path = "./ws2p", optional = true }
+duniter-tui = { path = "./tui", optional = true }
+duniter-ws2p = { path = "./ws2p" }
 
 [features]
-default = ["ws2p"]
-ws2p = ["duniter-ws2p"]
+default = ["tui","ssl"]
+ssl = ["duniter-ws2p/ssl"]
+tui = ["duniter-tui"]
 # Treat warnings as a build error.
 strict = []
 
-
 [workspace]
 members = [
     "blockchain",
diff --git a/src/main.rs b/src/main.rs
index d8e7d606e6bacb619f58c9ba45988f829ec32c46..938abebfb094f4ad0d98440ec975644e4859872f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -23,13 +23,13 @@
 )]
 
 extern crate duniter_core;
+#[cfg(feature = "tui")]
 extern crate duniter_tui;
-#[cfg(feature = "ws2p")]
 extern crate duniter_ws2p;
 
 pub use duniter_core::DuniterCore;
+#[cfg(feature = "tui")]
 pub use duniter_tui::TuiModule;
-#[cfg(feature = "ws2p")]
 pub use duniter_ws2p::WS2PModule;
 
 /// Main function
@@ -45,16 +45,16 @@ fn main() {
         //duniter_core.plug::<GvaModule>();
         //duniter_core.plug::<PoolModule>();
         //duniter_core.plug::<PowModule>();
-        duniter_core.plug::<TuiModule>();
-        plug_ws2p_module(&mut duniter_core);
+        plug_tui_module(&mut duniter_core);
+        duniter_core.plug::<WS2PModule>();
         duniter_core.start_blockchain();
     };
 }
 
-/// Plug WS2P Module
-#[cfg(feature = "ws2p")]
-fn plug_ws2p_module(duniter_core: &mut DuniterCore) {
-    duniter_core.plug::<WS2PModule>();
+/// Plug TUI Module
+#[cfg(feature = "tui")]
+fn plug_tui_module(duniter_core: &mut DuniterCore) {
+    duniter_core.plug::<TuiModule>();
 }
-#[cfg(not(feature = "ws2p"))]
-fn plug_ws2p_module(_duniter_core: &mut DuniterCore) {}
+#[cfg(not(feature = "tui"))]
+fn plug_tui_module(_duniter_core: &mut DuniterCore) {}
diff --git a/ws2p/Cargo.toml b/ws2p/Cargo.toml
index 955f268c5a60cc526dfc1280b56fd190bb860d51..89e831f711682a8f1323dee5eb20e6f7620a9ef8 100644
--- a/ws2p/Cargo.toml
+++ b/ws2p/Cargo.toml
@@ -26,8 +26,9 @@ sqlite = "0.23.9"
 serde = "1.0.24"
 serde_derive = "1.0.24"
 serde_json = "1.0.9"
-websocket = "0.20.2"
+ws = "0.7.6"
 
 [features]
+ssl = ["ws/ssl"]
 # Treat warnings as a build error.
 strict = []
\ No newline at end of file
diff --git a/ws2p/constants.rs b/ws2p/constants.rs
index 7d33b80de435b96c4fc4d93a3c79637aa932b5c7..bc8844462cb4e4e94db783f0ce47d604b17d3c69 100644
--- a/ws2p/constants.rs
+++ b/ws2p/constants.rs
@@ -13,9 +13,9 @@ pub static WS2P_OUTCOMING_INTERVAL: &'static u64 = &300;
 pub static WS2P_DEFAULT_OUTCOMING_QUOTA: &'static usize = &10;
 pub static WS2P_NEGOTIATION_TIMEOUT: &'static u64 = &15;
 //pub static WS2P_REQUEST_TIMEOUT : &'static u64 = &30;
-pub static WS2P_CONNECTION_TIMEOUT: &'static u64 = &120;
+pub static WS2P_EXPIRE_TIMEOUT: &'static u64 = &120;
 pub static WS2P_SPAM_INTERVAL_IN_MILLI_SECS: &'static u64 = &80;
-pub static WS2P_SPAM_LIMIT: &'static u64 = &6;
+pub static WS2P_SPAM_LIMIT: &'static usize = &6;
 pub static WS2P_SPAM_SLEEP_TIME_IN_SEC: &'static u64 = &100;
 pub static DURATION_BEFORE_RECORDING_ENDPOINT: &'static u64 = &180;
 pub static BLOCKS_REQUEST_INTERVAL: &'static u64 = &60;
diff --git a/ws2p/datas.rs b/ws2p/datas.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c9341419c3cf86284262d715736b06c6410bc96f
--- /dev/null
+++ b/ws2p/datas.rs
@@ -0,0 +1,576 @@
+//  Copyright (C) 2018  The Duniter Project Developers.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+use constants::*;
+use duniter_crypto::keys::*;
+use duniter_dal::dal_requests::DALRequest;
+use duniter_documents::Blockstamp;
+use duniter_message::DuniterMessage;
+use duniter_network::network_endpoint::*;
+use duniter_network::network_head::*;
+use duniter_network::*;
+use std::sync::mpsc;
+use *;
+
+#[derive(Debug)]
+pub struct WS2PModuleDatas {
+    pub followers: Vec<mpsc::Sender<DuniterMessage>>,
+    pub currency: Option<String>,
+    pub key_pair: Option<KeyPairEnum>,
+    pub conf: Option<WS2PConf>,
+    pub main_thread_channel: (
+        mpsc::Sender<WS2PThreadSignal>,
+        mpsc::Receiver<WS2PThreadSignal>,
+    ),
+    pub ws2p_endpoints: HashMap<NodeFullId, (NetworkEndpoint, WS2PConnectionState)>,
+    pub websockets: HashMap<NodeFullId, WsSender>,
+    pub requests_awaiting_response: HashMap<ModuleReqId, (NetworkRequest, NodeFullId, SystemTime)>,
+    pub heads_cache: HashMap<NodeFullId, NetworkHead>,
+    pub my_head: Option<NetworkHead>,
+    pub uids_cache: HashMap<PubKey, String>,
+}
+
+impl WS2PModuleDatas {
+    pub fn open_db(db_path: &PathBuf) -> Result<sqlite::Connection, sqlite::Error> {
+        let conn: sqlite::Connection;
+        if !db_path.as_path().exists() {
+            conn = sqlite::open(db_path.as_path())?;
+            conn.execute(
+                "CREATE TABLE endpoints (hash_full_id TEXT, status INTEGER, node_id INTEGER, pubkey TEXT,
+                api INTEGER, version INTEGER, endpoint TEXT, last_check INTEGER);",
+            )?;
+        } else {
+            conn = sqlite::open(db_path.as_path())?;
+        }
+        Ok(conn)
+    }
+    pub fn parse_ws2p_conf(
+        duniter_conf: &DuniterConf,
+        ws2p_json_conf: &serde_json::Value,
+    ) -> WS2PConf {
+        let mut sync_endpoints = Vec::new();
+        match ws2p_json_conf.get("sync_peers") {
+            Some(peers) => {
+                let array_peers = peers.as_array().expect("Conf: Fail to parse conf file !");
+                for peer in array_peers {
+                    let pubkey = match peer.get("pubkey") {
+                        Some(pubkey) => PubKey::Ed25519(
+                            ed25519::PublicKey::from_base58(
+                                pubkey
+                                    .as_str()
+                                    .expect("WS2PConf Error : fail to parse sync endpoint pubkey"),
+                            ).expect("WS2PConf Error : fail to parse sync endpoint pubkey"),
+                        ),
+                        None => panic!(
+                            "Fail to load ws2p conf : \
+                             WrongFormat : not found pubkey field !"
+                        ),
+                    };
+                    match peer.get("ws2p_endpoints") {
+                        Some(endpoints) => {
+                            let array_endpoints = endpoints
+                                .as_array()
+                                .expect("Conf: Fail to parse conf file !");
+                            for endpoint in array_endpoints {
+                                sync_endpoints.push(
+                                    NetworkEndpoint::parse_from_raw(
+                                        endpoint.as_str().expect("WS2P: Fail to get ep.as_str() !"),
+                                        pubkey,
+                                        0,
+                                        0,
+                                    ).expect(&format!(
+                                        "WS2PConf Error : fail to parse sync Endpoint = {:?}",
+                                        endpoint.as_str().expect("WS2P: Fail to get ep.as_str() !")
+                                    )),
+                                );
+                            }
+                        }
+                        None => panic!(
+                            "Fail to load conf : \
+                             WrongFormat : not found ws2p_endpoints field !"
+                        ),
+                    };
+                }
+            }
+            None => panic!(
+                "Configuration Error : \
+                 You must declare at least one node on which to synchronize !"
+            ),
+        };
+        WS2PConf {
+            outcoming_quota: *WS2P_DEFAULT_OUTCOMING_QUOTA,
+            node_id: NodeUUID(duniter_conf.my_node_id()),
+            sync_endpoints,
+        }
+    }
+    pub fn send_dal_request(&self, req: &DALRequest) {
+        for follower in &self.followers {
+            if follower
+                .send(DuniterMessage::DALRequest(req.clone()))
+                .is_err()
+            {
+                // handle error
+            }
+        }
+    }
+    pub fn send_network_event(&self, event: &NetworkEvent) {
+        for follower in &self.followers {
+            match follower.send(DuniterMessage::NetworkEvent(event.clone())) {
+                Ok(_) => {
+                    debug!("Send NetworkEvent to one follower.");
+                }
+                Err(_) => {
+                    warn!("Fail to send NetworkEvent to one follower !");
+                }
+            }
+        }
+    }
+    pub fn get_network_consensus(&self) -> Result<Blockstamp, NetworkConsensusError> {
+        let mut count_known_blockstamps = 0;
+        let mut farthest_blockstamp = Blockstamp::default();
+        let mut blockstamps_occurences: HashMap<Blockstamp, usize> =
+            HashMap::with_capacity(*WS2P_DEFAULT_OUTCOMING_QUOTA);
+        let mut dominant_blockstamp = Blockstamp::default();
+        let mut dominant_blockstamp_occurences = 0;
+        for (_ws2p_full_id, head) in self.heads_cache.clone() {
+            count_known_blockstamps += 1;
+            let blockstamps_occurences_copy = blockstamps_occurences.clone();
+            match blockstamps_occurences_copy.get(&head.blockstamp()) {
+                Some(occurences) => {
+                    let mut occurences_mut = blockstamps_occurences
+                        .get_mut(&head.blockstamp())
+                        .expect("WS2P: Fail to get_mut blockstamps_occurences !");
+                    *occurences_mut += 1;
+                    if *occurences > dominant_blockstamp_occurences {
+                        dominant_blockstamp_occurences = *occurences;
+                        dominant_blockstamp = head.blockstamp();
+                    }
+                }
+                None => {
+                    blockstamps_occurences.insert(head.blockstamp(), 0);
+                }
+            }
+            if head.blockstamp().id.0 > farthest_blockstamp.id.0 {
+                farthest_blockstamp = head.blockstamp();
+            }
+        }
+        if count_known_blockstamps < 5 {
+            return Err(NetworkConsensusError::InsufficientData(
+                count_known_blockstamps,
+            ));
+        } else if farthest_blockstamp == dominant_blockstamp {
+            return Ok(dominant_blockstamp);
+        }
+        Err(NetworkConsensusError::Fork())
+    }
+    fn count_established_connections(&self) -> usize {
+        let mut count_established_connections = 0;
+        for (_ws2p_full_id, (_ep, state)) in self.ws2p_endpoints.clone() {
+            if let WS2PConnectionState::Established = state {
+                count_established_connections += 1;
+            }
+        }
+        count_established_connections
+    }
+    pub fn connect_to_know_endpoints(&mut self) -> () {
+        info!("WS2P: connect to know endpoints...");
+        let mut count_established_connections = 0;
+        let mut reachable_endpoints = Vec::new();
+        let mut unreachable_endpoints = Vec::new();
+        for (_ws2p_full_id, (ep, state)) in self.ws2p_endpoints.clone() {
+            match state {
+                WS2PConnectionState::Established => count_established_connections += 1,
+                WS2PConnectionState::NeverTry
+                | WS2PConnectionState::Close
+                | WS2PConnectionState::Denial => reachable_endpoints.push(ep),
+                _ => unreachable_endpoints.push(ep),
+            }
+        }
+        let mut free_outcoming_rooms = self
+            .conf
+            .clone()
+            .expect("WS2P: Fail to get conf !")
+            .outcoming_quota - count_established_connections;
+        while free_outcoming_rooms > 0 {
+            let ep = if !reachable_endpoints.is_empty() {
+                reachable_endpoints
+                    .pop()
+                    .expect("WS2P: Fail to pop() reachable_endpoints !")
+            } else if !unreachable_endpoints.is_empty() {
+                unreachable_endpoints
+                    .pop()
+                    .expect("WS2P: Fail to pop() unreachable_endpoints !")
+            } else {
+                break;
+            };
+            if cfg!(feature = "ssl") || ep.port() != 443 {
+                self.connect_to_without_checking_quotas(&ep);
+                free_outcoming_rooms -= 1;
+            }
+        }
+    }
+    pub fn connect_to(&mut self, endpoint: &NetworkEndpoint) -> () {
+        // Add endpoint to endpoints list (if there isn't already)
+        match self.ws2p_endpoints.get(
+            &endpoint
+                .node_full_id()
+                .expect("WS2P: Fail to get ep.node_full_id() !"),
+        ) {
+            Some(_) => {
+                self.ws2p_endpoints
+                    .get_mut(
+                        &endpoint
+                            .node_full_id()
+                            .expect("WS2P: Fail to get ep.node_full_id() !"),
+                    )
+                    .expect("WS2P: Fail to get_mut() a ws2p_endpoint !")
+                    .1 = WS2PConnectionState::NeverTry;
+            }
+            None => {
+                self.ws2p_endpoints.insert(
+                    endpoint
+                        .node_full_id()
+                        .expect("WS2P: Fail to get ep.node_full_id() !"),
+                    (endpoint.clone(), WS2PConnectionState::NeverTry),
+                );
+            }
+        };
+        if self
+            .conf
+            .clone()
+            .expect("WS2P: Fail to get conf !")
+            .outcoming_quota > self.count_established_connections()
+        {
+            self.connect_to_without_checking_quotas(&endpoint);
+        }
+    }
+    fn close_connection(&mut self, ws2p_full_id: &NodeFullId, reason: WS2PCloseConnectionReason) {
+        match reason {
+            WS2PCloseConnectionReason::NegociationTimeout => {}
+            WS2PCloseConnectionReason::AuthMessInvalidSig
+            | WS2PCloseConnectionReason::Timeout
+            | WS2PCloseConnectionReason::Unknow => {
+                self.ws2p_endpoints
+                    .get_mut(ws2p_full_id)
+                    .expect("Failure : attempt to delete a non-existent connection !")
+                    .1 = WS2PConnectionState::Close
+            }
+        }
+        let _result = self
+            .websockets
+            .get(&ws2p_full_id)
+            .expect("Try to close an unexistant websocket !")
+            .0
+            .close(ws::CloseCode::Normal);
+        self.websockets.remove(ws2p_full_id).expect(&format!(
+            "Fatal error : no websocket for {} !",
+            ws2p_full_id
+        ));;
+    }
+    pub fn ws2p_conn_message_pretreatment(&mut self, message: WS2PConnectionMessage) -> WS2PSignal {
+        let ws2p_full_id = message.0;
+        match message.1 {
+            WS2PConnectionMessagePayload::WrongUrl
+            | WS2PConnectionMessagePayload::FailOpenWS
+            | WS2PConnectionMessagePayload::FailToSplitWS => {
+                self.ws2p_endpoints
+                    .get_mut(&ws2p_full_id)
+                    .expect("WS2P: Fail to get mut ep !")
+                    .1 = WS2PConnectionState::WSError;
+                return WS2PSignal::WSError(ws2p_full_id);
+            }
+            WS2PConnectionMessagePayload::TryToSendConnectMess => {
+                self.ws2p_endpoints
+                    .get_mut(&ws2p_full_id)
+                    .expect("WS2P: Fail to get mut ep !")
+                    .1 = WS2PConnectionState::TryToSendConnectMess;
+            }
+            WS2PConnectionMessagePayload::FailSendConnectMess => {
+                self.ws2p_endpoints
+                    .get_mut(&ws2p_full_id)
+                    .expect("WS2P: Fail to mut ep !")
+                    .1 = WS2PConnectionState::Unreachable;
+            }
+            WS2PConnectionMessagePayload::WebsocketOk(sender) => {
+                self.websockets.insert(ws2p_full_id, sender);
+            }
+            WS2PConnectionMessagePayload::ValidConnectMessage(response, new_con_state) => {
+                self.ws2p_endpoints
+                    .get_mut(&ws2p_full_id)
+                    .expect("WS2P: Fail to get mut ep !")
+                    .1 = new_con_state;
+                self.ws2p_endpoints
+                    .get_mut(&ws2p_full_id)
+                    .expect("Endpoint don't exist !")
+                    .1 = WS2PConnectionState::ConnectMessOk;
+                debug!("Send: {:#?}", response);
+                self.websockets
+                    .get_mut(&ws2p_full_id)
+                    .expect(&format!(
+                        "Fatal error : no websocket for {} !",
+                        ws2p_full_id
+                    ))
+                    .0
+                    .send(Message::text(response))
+                    .expect("WS2P: Fail to send OK Message !");
+            }
+            WS2PConnectionMessagePayload::ValidAckMessage(r, new_con_state) => {
+                self.ws2p_endpoints
+                    .get_mut(&ws2p_full_id)
+                    .expect("WS2P: Fail to get mut ep !")
+                    .1 = new_con_state;
+                if let WS2PConnectionState::AckMessOk = self.ws2p_endpoints[&ws2p_full_id].1 {
+                    trace!("DEBUG : Send: {:#?}", r);
+                    self.websockets
+                        .get_mut(&ws2p_full_id)
+                        .expect(&format!(
+                            "Fatal error : no websocket for {} !",
+                            ws2p_full_id
+                        ))
+                        .0
+                        .send(Message::text(r))
+                        .expect("WS2P: Fail to send Message in websocket !");
+                }
+            }
+            WS2PConnectionMessagePayload::ValidOk(new_con_state) => {
+                self.ws2p_endpoints
+                    .get_mut(&ws2p_full_id)
+                    .expect("WS2P: Fail to get mut ep !")
+                    .1 = new_con_state;
+                match self.ws2p_endpoints[&ws2p_full_id].1 {
+                    WS2PConnectionState::OkMessOkWaitingAckMess => {}
+                    WS2PConnectionState::Established => {
+                        return WS2PSignal::ConnectionEstablished(ws2p_full_id)
+                    }
+                    _ => {
+                        self.close_connection(&ws2p_full_id, WS2PCloseConnectionReason::Unknow);
+                        return WS2PSignal::Empty;
+                    }
+                }
+            }
+            WS2PConnectionMessagePayload::DalRequest(req_id, req_body) => {
+                return WS2PSignal::DalRequest(ws2p_full_id, req_id, req_body);
+            }
+            WS2PConnectionMessagePayload::PeerCard(body, ws2p_endpoints) => {
+                return WS2PSignal::PeerCard(ws2p_full_id, body, ws2p_endpoints);
+            }
+            WS2PConnectionMessagePayload::Heads(heads) => {
+                let mut applied_heads = Vec::with_capacity(heads.len());
+                for head in heads {
+                    if let Some(head) = NetworkHead::from_json_value(&head) {
+                        if head.verify()
+                            && (self.my_head.is_none()
+                                || head.node_full_id()
+                                    != self
+                                        .my_head
+                                        .clone()
+                                        .expect("WS2P: Fail to clone my_head")
+                                        .node_full_id())
+                            && head.apply(&mut self.heads_cache)
+                        {
+                            applied_heads.push(head);
+                        }
+                    }
+                }
+                return WS2PSignal::Heads(ws2p_full_id, applied_heads);
+            }
+            WS2PConnectionMessagePayload::Document(network_doc) => {
+                return WS2PSignal::Document(ws2p_full_id, network_doc);
+            }
+            WS2PConnectionMessagePayload::ReqResponse(req_id, response) => {
+                if self.requests_awaiting_response.len() > req_id.0 as usize {
+                    if let Some((ref ws2p_request, ref recipient_fulld_id, ref _timestamp)) =
+                        self.requests_awaiting_response.remove(&req_id)
+                    {
+                        return WS2PSignal::ReqResponse(
+                            req_id,
+                            *ws2p_request,
+                            *recipient_fulld_id,
+                            response,
+                        );
+                    }
+                }
+            }
+            WS2PConnectionMessagePayload::NegociationTimeout => {
+                match self.ws2p_endpoints[&ws2p_full_id].1 {
+                    WS2PConnectionState::AckMessOk | WS2PConnectionState::ConnectMessOk => {
+                        self.ws2p_endpoints
+                            .get_mut(&ws2p_full_id)
+                            .expect("WS2P: Fail to get mut ep !")
+                            .1 = WS2PConnectionState::Denial
+                    }
+                    WS2PConnectionState::WaitingConnectMess => {
+                        self.ws2p_endpoints
+                            .get_mut(&ws2p_full_id)
+                            .expect("WS2P: Fail to get mut ep !")
+                            .1 = WS2PConnectionState::NoResponse
+                    }
+                    _ => {
+                        self.ws2p_endpoints
+                            .get_mut(&ws2p_full_id)
+                            .expect("WS2P: Fail to get mut ep !")
+                            .1 = WS2PConnectionState::Unreachable
+                    }
+                }
+                self.close_connection(&ws2p_full_id, WS2PCloseConnectionReason::NegociationTimeout);
+                return WS2PSignal::NegociationTimeout(ws2p_full_id);
+            }
+            WS2PConnectionMessagePayload::Timeout => {
+                self.close_connection(&ws2p_full_id, WS2PCloseConnectionReason::Timeout);
+                return WS2PSignal::Timeout(ws2p_full_id);
+            }
+            WS2PConnectionMessagePayload::UnknowMessage => {
+                warn!("WS2P : Receive Unknow Message from {}.", &ws2p_full_id.1)
+            }
+            WS2PConnectionMessagePayload::WrongFormatMessage => warn!(
+                "WS2P : Receive Wrong Format Message from {}.",
+                &ws2p_full_id.1
+            ),
+            WS2PConnectionMessagePayload::InvalidMessage => return WS2PSignal::Empty,
+            WS2PConnectionMessagePayload::Close => {
+                if self.websockets.contains_key(&ws2p_full_id) {
+                    self.close_connection(
+                        &ws2p_full_id,
+                        WS2PCloseConnectionReason::AuthMessInvalidSig,
+                    )
+                }
+            }
+        }
+        let connections_count = self.websockets.len();
+        if connections_count == 0 {
+            return WS2PSignal::NoConnection;
+        }
+        // Detect timeout requests
+        let mut requests_timeout = Vec::new();
+        for &(ref req, ref _ws2p_full_id, ref timestamp) in
+            self.requests_awaiting_response.clone().values()
+        {
+            if SystemTime::now().duration_since(*timestamp).unwrap() > Duration::new(20, 0) {
+                requests_timeout.push(req.get_req_full_id());
+                warn!("request timeout : {:?}", req);
+            }
+        }
+        // Delete (and resend) timeout requests
+        for req_id in requests_timeout {
+            //let ws2p_endpoints = self.ws2p_endpoints.clone();
+            let _request_option = self.requests_awaiting_response.remove(&req_id.1);
+            /*if let Some((request, _, _)) = request_option {
+                let _request_result = self.send_request_to_specific_node(
+                    &get_random_connection(&ws2p_endpoints),
+                    &request,
+                );
+            }*/
+        }
+        WS2PSignal::Empty
+    }
+
+    /*pub fn send_request_to_all_connections(
+        &mut self,
+        ws2p_request: &NetworkRequest,
+    ) -> Result<(), SendRequestError> {
+        let mut count_successful_sending: usize = 0;
+        let mut errors: Vec<ws::Error> = Vec::new();
+        match *ws2p_request {
+            NetworkRequest::GetCurrent(req_full_id, _receiver) => {
+                for (ws2p_full_id, (_ep, state)) in self.ws2p_endpoints.clone() {
+                    if let WS2PConnectionState::Established = state {
+                        let ws2p_request = NetworkRequest::GetCurrent(
+                            ModuleReqFullId(
+                                req_full_id.0,
+                                ModuleReqId(
+                                    (self.requests_awaiting_response.len()
+                                        + count_successful_sending)
+                                        as u32,
+                                ),
+                            ),
+                            ws2p_full_id,
+                        );
+                        match self.send_request_to_specific_node(&ws2p_full_id, &ws2p_request) {
+                            Ok(_) => count_successful_sending += 1,
+                            Err(e) => errors.push(e),
+                        };
+                    }
+                }
+            }
+            /* NetworkRequest::GetBlock(req_full_id, number) => {} */
+            NetworkRequest::GetBlocks(_req_full_id, _receiver, _count, _from_number) => {}
+            NetworkRequest::GetRequirementsPending(req_full_id, _receiver, min_cert) => {
+                for (ws2p_full_id, (_ep, state)) in self.ws2p_endpoints.clone() {
+                    if let WS2PConnectionState::Established = state {
+                        let ws2p_request = NetworkRequest::GetRequirementsPending(
+                            ModuleReqFullId(
+                                req_full_id.0,
+                                ModuleReqId(self.requests_awaiting_response.len() as u32),
+                            ),
+                            ws2p_full_id,
+                            min_cert,
+                        );
+                        match self.send_request_to_specific_node(&ws2p_full_id, &ws2p_request) {
+                            Ok(_) => count_successful_sending += 1,
+                            Err(e) => errors.push(e),
+                        };
+                    }
+                }
+            }
+            _ => {
+                return Err(SendRequestError::RequestTypeMustNotBeTransmitted());
+            }
+        }
+        debug!("count_successful_sending = {}", count_successful_sending);
+        if !errors.is_empty() {
+            return Err(SendRequestError::WSError(count_successful_sending, errors));
+        }
+        Ok(())
+    }*/
+
+    pub fn send_request_to_specific_node(
+        &mut self,
+        receiver_ws2p_full_id: &NodeFullId,
+        ws2p_request: &NetworkRequest,
+    ) -> ws::Result<()> {
+        self.websockets
+            .get_mut(receiver_ws2p_full_id)
+            .expect("WS2P: Fail to get mut websocket !")
+            .0
+            .send(Message::text(
+                network_request_to_json(ws2p_request).to_string(),
+            ))?;
+        self.requests_awaiting_response.insert(
+            ws2p_request.get_req_id(),
+            (*ws2p_request, *receiver_ws2p_full_id, SystemTime::now()),
+        );
+        debug!(
+            "send request {} to {}",
+            network_request_to_json(ws2p_request).to_string(),
+            receiver_ws2p_full_id
+        );
+        Ok(())
+    }
+
+    fn connect_to_without_checking_quotas(&mut self, endpoint: &NetworkEndpoint) {
+        let endpoint_copy = endpoint.clone();
+        let conductor_sender_copy = self.main_thread_channel.0.clone();
+        let currency_copy = self.currency.clone();
+        let key_pair_copy = self.key_pair;
+        thread::spawn(move || {
+            let _result = connect_to_ws2p_endpoint(
+                &endpoint_copy,
+                &conductor_sender_copy,
+                &currency_copy.expect("WS2PError : No currency !"),
+                key_pair_copy.expect("WS2PError : No key_pair !"),
+            );
+        });
+    }
+}
diff --git a/ws2p/heads.rs b/ws2p/heads.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1ed46725874a89d572587b7aa101e0072a3a05a7
--- /dev/null
+++ b/ws2p/heads.rs
@@ -0,0 +1,64 @@
+//  Copyright (C) 2018  The Duniter Project Developers.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+use duniter_documents::Blockstamp;
+use duniter_network::network_head::*;
+use *;
+
+pub fn generate_my_head(
+    network_keypair: &KeyPairEnum,
+    conf: &WS2PConf,
+    soft_name: &str,
+    soft_version: &str,
+    my_current_blockstamp: &Blockstamp,
+    my_uid: Option<String>,
+) -> NetworkHead {
+    let message = NetworkHeadMessage::V2(NetworkHeadMessageV2 {
+        api: String::from("WS2POCA"),
+        version: 1,
+        pubkey: network_keypair.public_key(),
+        blockstamp: *my_current_blockstamp,
+        node_uuid: conf.node_id,
+        software: String::from(soft_name),
+        soft_version: String::from(soft_version),
+        prefix: 1,
+        free_member_room: None,
+        free_mirror_room: None,
+    });
+    let message_v2 = NetworkHeadMessage::V2(NetworkHeadMessageV2 {
+        api: String::from("WS2POCA"),
+        version: 2,
+        pubkey: network_keypair.public_key(),
+        blockstamp: *my_current_blockstamp,
+        node_uuid: conf.node_id,
+        software: String::from(soft_name),
+        soft_version: String::from(soft_version),
+        prefix: 1,
+        free_member_room: Some(0),
+        free_mirror_room: Some(0),
+    });
+    NetworkHead::V2(Box::new(NetworkHeadV2 {
+        message: message.clone(),
+        sig: network_keypair
+            .private_key()
+            .sign(message.to_string().as_bytes()),
+        message_v2: message_v2.clone(),
+        sig_v2: network_keypair
+            .private_key()
+            .sign(message_v2.to_string().as_bytes()),
+        step: 0,
+        uid: my_uid,
+    }))
+}
diff --git a/ws2p/lib.rs b/ws2p/lib.rs
index dc696d57c50c293ba687b5496891f207b75c016a..035c2fc40ecbe836d33f1dce55697d6f97d0e931 100644
--- a/ws2p/lib.rs
+++ b/ws2p/lib.rs
@@ -41,31 +41,13 @@ extern crate duniter_module;
 extern crate duniter_network;
 extern crate rand;
 extern crate sqlite;
-extern crate websocket;
-
-use std::collections::HashMap;
-use std::ops::Deref;
-use std::path::PathBuf;
-use std::str::from_utf8;
-use std::sync::mpsc;
-use std::thread;
-use std::time::{Duration, SystemTime, UNIX_EPOCH};
-
-use duniter_crypto::keys::*;
-use duniter_dal::dal_event::DALEvent;
-use duniter_dal::dal_requests::{DALReqBlockchain, DALRequest, DALResBlockchain, DALResponse};
-use duniter_documents::Blockstamp;
-use duniter_message::DuniterMessage;
-use duniter_module::*;
-use duniter_network::network_endpoint::*;
-use duniter_network::network_head::*;
-use duniter_network::*;
-use parsers::blocks::parse_json_block;
-use websocket::{ClientBuilder, Message};
+extern crate ws;
 
 mod ack_message;
 mod connect_message;
 pub mod constants;
+mod datas;
+mod heads;
 mod ok_message;
 pub mod parsers;
 pub mod serializer;
@@ -76,26 +58,45 @@ pub mod ws2p_requests;
 use ack_message::WS2PAckMessageV1;
 use connect_message::WS2PConnectMessageV1;
 use constants::*;
+use datas::*;
+use duniter_crypto::keys::*;
+use duniter_dal::dal_event::DALEvent;
+use duniter_dal::dal_requests::{DALReqBlockchain, DALRequest, DALResBlockchain, DALResponse};
+use duniter_documents::Blockstamp;
+use duniter_message::DuniterMessage;
+use duniter_module::*;
+use duniter_network::network_endpoint::*;
+use duniter_network::network_head::*;
+use duniter_network::*;
 use ok_message::WS2POkMessageV1;
-use rand::Rng;
+use parsers::blocks::parse_json_block;
+use std::collections::HashMap;
+use std::ops::Deref;
+use std::path::PathBuf;
+use std::sync::mpsc;
+use std::thread;
+use std::time::{Duration, SystemTime, UNIX_EPOCH};
+use ws::Message;
 use ws2p_connection::*;
 use ws2p_requests::network_request_to_json;
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+/// WS2P Configuration
 pub struct WS2PConf {
+    /// Node unique identifier
     pub node_id: NodeUUID,
+    /// Limit of outcoming connections
     pub outcoming_quota: usize,
+    /// Default WS2P endpoints provides by configuration file
     pub sync_endpoints: Vec<NetworkEndpoint>,
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub enum WS2PConfParseError {
-    UnknowError(),
-}
-
 #[derive(Debug)]
+/// Store a Signal receive from network (after message treatment)
 pub enum WS2PSignal {
+    /// Receive a websocket error from a connextion. `NodeFullId` store the identifier of connection.
     WSError(NodeFullId),
+    /// A new connection is successfully established with `NodeFullId`.
     ConnectionEstablished(NodeFullId),
     NegociationTimeout(NodeFullId),
     Timeout(NodeFullId),
@@ -117,32 +118,12 @@ pub enum NetworkConsensusError {
 #[derive(Debug)]
 pub enum SendRequestError {
     RequestTypeMustNotBeTransmitted(),
-    WSError(usize, Vec<websocket::WebSocketError>),
+    WSError(usize, Vec<ws::Error>),
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct WS2PModule {}
 
-#[derive(Debug)]
-pub struct WS2PModuleDatas {
-    pub followers: Vec<mpsc::Sender<DuniterMessage>>,
-    pub currency: Option<String>,
-    pub key_pair: Option<KeyPairEnum>,
-    pub conf: Option<WS2PConf>,
-    pub main_thread_channel: (
-        mpsc::Sender<WS2PThreadSignal>,
-        mpsc::Receiver<WS2PThreadSignal>,
-    ),
-    pub ws2p_endpoints: HashMap<NodeFullId, (NetworkEndpoint, WS2PConnectionState)>,
-    pub connections_meta_datas: HashMap<NodeFullId, WS2PConnectionMetaData>,
-    pub websockets: HashMap<NodeFullId, WebsocketSender>,
-    pub threads_senders_channels: HashMap<NodeFullId, mpsc::Sender<WS2POrderForListeningThread>>,
-    pub requests_awaiting_response: HashMap<ModuleReqId, (NetworkRequest, NodeFullId, SystemTime)>,
-    pub heads_cache: HashMap<NodeFullId, NetworkHead>,
-    pub my_head: Option<NetworkHead>,
-    pub uids_cache: HashMap<PubKey, String>,
-}
-
 #[derive(Debug)]
 pub enum WS2PThreadSignal {
     DuniterMessage(Box<DuniterMessage>),
@@ -159,26 +140,6 @@ pub trait WS2PMessage: Sized {
     //fn parse_and_verify(v: serde_json::Value, currency: String) -> bool;
 }
 
-pub fn get_random_connection<S: ::std::hash::BuildHasher>(
-    connections: &HashMap<NodeFullId, (NetworkEndpoint, WS2PConnectionState), S>,
-) -> NodeFullId {
-    let mut rng = rand::thread_rng();
-    let mut loop_count = 0;
-    loop {
-        for (ws2p_full_id, (_ep, state)) in &(*connections) {
-            if loop_count > 10 {
-                return *ws2p_full_id;
-            }
-            if let WS2PConnectionState::Established = state {
-                if rng.gen::<bool>() {
-                    return *ws2p_full_id;
-                }
-            }
-        }
-        loop_count += 1;
-    }
-}
-
 impl Default for WS2PModule {
     fn default() -> WS2PModule {
         WS2PModule {}
@@ -198,6 +159,9 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
     fn default_conf() -> serde_json::Value {
         json!({
             "sync_peers": [{
+                "pubkey": "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx",
+                "ws2p_endpoints": ["WS2P c1c39a0a i3.ifee.fr 80 /ws2p"]
+            },{
                 "pubkey": "7v2J4badvfWQ6qwRdCwhhJfAsmKwoxRUNpJHiJHj7zef",
                 "ws2p_endpoints": ["WS2P b48824f0 g1.monnaielibreoccitanie.org 80 /ws2p"]
             }]
@@ -220,9 +184,7 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
             conf: None,
             main_thread_channel: mpsc::channel(),
             ws2p_endpoints: HashMap::new(),
-            connections_meta_datas: HashMap::new(),
             websockets: HashMap::new(),
-            threads_senders_channels: HashMap::new(),
             requests_awaiting_response: HashMap::new(),
             heads_cache: HashMap::new(),
             my_head: None,
@@ -294,7 +256,9 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
         let dal_enpoints =
             ws2p_db::get_endpoints_for_api(&db, &NetworkEndpointApi(String::from("WS2P")));
         for ep in dal_enpoints {
-            if ep.api() == NetworkEndpointApi(String::from("WS2P")) && ep.port() != 443 {
+            if ep.api() == NetworkEndpointApi(String::from("WS2P"))
+                && (cfg!(feature = "ssl") || ep.port() != 443)
+            {
                 count += 1;
                 ws2p_module.ws2p_endpoints.insert(
                     ep.node_full_id()
@@ -347,15 +311,14 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
                                         );
                                     } else {
                                         if ws2p_module.my_head.is_none() {
-                                            ws2p_module.my_head =
-                                                Some(WS2PModuleDatas::generate_my_head(
-                                                    &key_pair,
-                                                    &conf.clone(),
-                                                    soft_name,
-                                                    soft_version,
-                                                    &current_blockstamp,
-                                                    None,
-                                                ));
+                                            ws2p_module.my_head = Some(heads::generate_my_head(
+                                                &key_pair,
+                                                &conf.clone(),
+                                                soft_name,
+                                                soft_version,
+                                                &current_blockstamp,
+                                                None,
+                                            ));
                                         }
                                         ws2p_module.send_network_event(
                                             &NetworkEvent::ReceiveHeads(vec![
@@ -407,8 +370,6 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
                                                         *req_id, *receiver, *count, *from,
                                                     ),
                                                 );
-                                        } else {
-                                            warn!("WS2PModule : No WS2P connections !");
                                         }
                                     } else {
                                         let _blocks_request_result = ws2p_module
@@ -430,7 +391,7 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
                                         "WS2PModule : current_blockstamp = {}",
                                         current_blockstamp
                                     );
-                                    ws2p_module.my_head = Some(WS2PModuleDatas::generate_my_head(
+                                    ws2p_module.my_head = Some(heads::generate_my_head(
                                         &key_pair,
                                         &conf.clone(),
                                         soft_name,
@@ -448,12 +409,12 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
                                     trace!("Send my HEAD: {:#?}", my_json_head);
                                     let _results: Result<
                                         (),
-                                        websocket::WebSocketError,
+                                        ws::Error,
                                     > = ws2p_module
                                         .websockets
                                         .iter_mut()
                                         .map(|ws| {
-                                            (ws.1).0.send_message(&Message::text(
+                                            (ws.1).0.send(Message::text(
                                                 json!({
                                                 "name": "HEAD",
                                                 "body": {
@@ -483,7 +444,7 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
                                             current_blockstamp = *current_blockstamp_;
                                             if ws2p_module.my_head.is_none() {
                                                 ws2p_module.my_head =
-                                                    Some(WS2PModuleDatas::generate_my_head(
+                                                    Some(heads::generate_my_head(
                                                         &key_pair,
                                                         &conf.clone(),
                                                         soft_name,
@@ -526,14 +487,14 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
                                             );
                                             // Resent to other modules connections that match receive uids
                                             for (node_full_id, (ep, conn_state)) in
-                                                ws2p_module.ws2p_endpoints.clone()
+                                                &ws2p_module.ws2p_endpoints
                                             {
                                                 if let Some(uid_option) = uids.get(&node_full_id.1)
                                                 {
                                                     ws2p_module.send_network_event(
                                                         &NetworkEvent::ConnectionStateChange(
-                                                            node_full_id,
-                                                            conn_state as u32,
+                                                            *node_full_id,
+                                                            *conn_state as u32,
                                                             uid_option.clone(),
                                                             ep.get_url(false),
                                                         ),
@@ -554,8 +515,6 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
                     {
                         WS2PSignal::NoConnection => {
                             warn!("WS2PSignal::NoConnection");
-                            last_ws2p_connecting_wave = SystemTime::now();
-                            ws2p_module.connect_to_know_endpoints();
                         }
                         WS2PSignal::ConnectionEstablished(ws2p_full_id) => {
                             let req_id =
@@ -569,6 +528,11 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
                                         ws2p_full_id,
                                     ),
                                 );
+                            if ws2p_module.uids_cache.get(&ws2p_full_id.1).is_none() {
+                                ws2p_module.send_dal_request(&DALRequest::BlockchainRequest(
+                                    DALReqBlockchain::UIDs(vec![ws2p_full_id.1]),
+                                ));
+                            }
                             ws2p_module.send_network_event(&NetworkEvent::ConnectionStateChange(
                                 ws2p_full_id,
                                 WS2PConnectionState::Established as u32,
@@ -607,27 +571,27 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
                             //trace!("WS2PSignal::PeerCard({})", ws2p_full_id);
                             //ws2p_module.send_network_event(NetworkEvent::ReceivePeers(_));
                             for ep in ws2p_endpoints {
-                                if ep.port() != 443 {
-                                    match ws2p_module.ws2p_endpoints.get(
-                                        &ep.node_full_id()
-                                            .expect("WS2P: Fail to get ep.node_full_id() !"),
-                                    ) {
-                                        Some(_) => {}
-                                        None => {
-                                            if let Some(_api) =
-                                                ws2p_db::string_to_api(&ep.api().0.clone())
-                                            {
-                                                endpoints_to_update_status.insert(
-                                                    ep.node_full_id().expect(
-                                                        "WS2P: Fail to get ep.node_full_id() !",
-                                                    ),
-                                                    SystemTime::now(),
-                                                );
-                                            }
+                                match ws2p_module.ws2p_endpoints.get(
+                                    &ep.node_full_id()
+                                        .expect("WS2P: Fail to get ep.node_full_id() !"),
+                                ) {
+                                    Some(_) => {}
+                                    None => {
+                                        if let Some(_api) =
+                                            ws2p_db::string_to_api(&ep.api().0.clone())
+                                        {
+                                            endpoints_to_update_status.insert(
+                                                ep.node_full_id().expect(
+                                                    "WS2P: Fail to get ep.node_full_id() !",
+                                                ),
+                                                SystemTime::now(),
+                                            );
+                                        }
+                                        if cfg!(feature = "ssl") || ep.port() != 443 {
                                             ws2p_module.connect_to(&ep);
                                         }
-                                    };
-                                }
+                                    }
+                                };
                             }
                         }
                         WS2PSignal::Heads(ws2p_full_id, heads) => {
@@ -669,13 +633,6 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
                                             )),
                                         ));
                                     }
-                                    /*if let Some(block) = BlockV10::from_json_value(&response) {
-                                        ws2p_module
-                                            .connections_meta_datas
-                                            .get_mut(&recipient_full_id)
-                                            .unwrap()
-                                            .current_blockstamp = Some((block.id, block.hash));
-                                    }*/
                                 }
                                 NetworkRequest::GetBlocks(ref _req_id, _receiver, _count, from) => {
                                     info!("WS2PSignal::ReceiveChunk({}, {:?})", req_id.0, req);
@@ -906,831 +863,6 @@ impl DuniterModule<DuniterMessage> for WS2PModule {
     }
 }
 
-impl WS2PModuleDatas {
-    fn open_db(db_path: &PathBuf) -> Result<sqlite::Connection, sqlite::Error> {
-        let conn: sqlite::Connection;
-        if !db_path.as_path().exists() {
-            conn = sqlite::open(db_path.as_path())?;
-            conn.execute(
-                "CREATE TABLE endpoints (hash_full_id TEXT, status INTEGER, node_id INTEGER, pubkey TEXT,
-                api INTEGER, version INTEGER, endpoint TEXT, last_check INTEGER);",
-            )?;
-        } else {
-            conn = sqlite::open(db_path.as_path())?;
-        }
-        Ok(conn)
-    }
-    pub fn parse_ws2p_conf(
-        duniter_conf: &DuniterConf,
-        ws2p_json_conf: &serde_json::Value,
-    ) -> WS2PConf {
-        let mut sync_endpoints = Vec::new();
-        match ws2p_json_conf.get("sync_peers") {
-            Some(peers) => {
-                let array_peers = peers.as_array().expect("Conf: Fail to parse conf file !");
-                for peer in array_peers {
-                    let pubkey = match peer.get("pubkey") {
-                        Some(pubkey) => PubKey::Ed25519(
-                            ed25519::PublicKey::from_base58(
-                                pubkey
-                                    .as_str()
-                                    .expect("WS2PConf Error : fail to parse sync endpoint pubkey"),
-                            ).expect("WS2PConf Error : fail to parse sync endpoint pubkey"),
-                        ),
-                        None => panic!(
-                            "Fail to load ws2p conf : \
-                             WrongFormat : not found pubkey field !"
-                        ),
-                    };
-                    match peer.get("ws2p_endpoints") {
-                        Some(endpoints) => {
-                            let array_endpoints = endpoints
-                                .as_array()
-                                .expect("Conf: Fail to parse conf file !");
-                            for endpoint in array_endpoints {
-                                sync_endpoints.push(
-                                    NetworkEndpoint::parse_from_raw(
-                                        endpoint.as_str().expect("WS2P: Fail to get ep.as_str() !"),
-                                        pubkey,
-                                        0,
-                                        0,
-                                    ).expect(&format!(
-                                        "WS2PConf Error : fail to parse sync Endpoint = {:?}",
-                                        endpoint.as_str().expect("WS2P: Fail to get ep.as_str() !")
-                                    )),
-                                );
-                            }
-                        }
-                        None => panic!(
-                            "Fail to load conf : \
-                             WrongFormat : not found ws2p_endpoints field !"
-                        ),
-                    };
-                }
-            }
-            None => panic!(
-                "Configuration Error : \
-                 You must declare at least one node on which to synchronize !"
-            ),
-        };
-        WS2PConf {
-            outcoming_quota: *WS2P_DEFAULT_OUTCOMING_QUOTA,
-            node_id: NodeUUID(duniter_conf.my_node_id()),
-            sync_endpoints,
-        }
-    }
-    pub fn send_dal_request(&self, req: &DALRequest) {
-        for follower in &self.followers {
-            if follower
-                .send(DuniterMessage::DALRequest(req.clone()))
-                .is_err()
-            {
-                // handle error
-            }
-        }
-    }
-    pub fn send_network_event(&self, event: &NetworkEvent) {
-        for follower in &self.followers {
-            match follower.send(DuniterMessage::NetworkEvent(event.clone())) {
-                Ok(_) => {
-                    debug!("Send NetworkEvent to one follower.");
-                }
-                Err(_) => {
-                    warn!("Fail to send NetworkEvent to one follower !");
-                }
-            }
-        }
-    }
-    pub fn generate_my_head(
-        network_keypair: &KeyPairEnum,
-        conf: &WS2PConf,
-        soft_name: &str,
-        soft_version: &str,
-        my_current_blockstamp: &Blockstamp,
-        my_uid: Option<String>,
-    ) -> NetworkHead {
-        let message = NetworkHeadMessage::V2(NetworkHeadMessageV2 {
-            api: String::from("WS2POCA"),
-            version: 1,
-            pubkey: network_keypair.public_key(),
-            blockstamp: *my_current_blockstamp,
-            node_uuid: conf.node_id,
-            software: String::from(soft_name),
-            soft_version: String::from(soft_version),
-            prefix: 1,
-            free_member_room: None,
-            free_mirror_room: None,
-        });
-        let message_v2 = NetworkHeadMessage::V2(NetworkHeadMessageV2 {
-            api: String::from("WS2POCA"),
-            version: 2,
-            pubkey: network_keypair.public_key(),
-            blockstamp: *my_current_blockstamp,
-            node_uuid: conf.node_id,
-            software: String::from(soft_name),
-            soft_version: String::from(soft_version),
-            prefix: 1,
-            free_member_room: Some(0),
-            free_mirror_room: Some(0),
-        });
-        NetworkHead::V2(Box::new(NetworkHeadV2 {
-            message: message.clone(),
-            sig: network_keypair
-                .private_key()
-                .sign(message.to_string().as_bytes()),
-            message_v2: message_v2.clone(),
-            sig_v2: network_keypair
-                .private_key()
-                .sign(message_v2.to_string().as_bytes()),
-            step: 0,
-            uid: my_uid,
-        }))
-    }
-    pub fn get_network_consensus(&self) -> Result<Blockstamp, NetworkConsensusError> {
-        let mut count_known_blockstamps = 0;
-        let mut farthest_blockstamp = Blockstamp::default();
-        let mut blockstamps_occurences: HashMap<Blockstamp, usize> =
-            HashMap::with_capacity(*WS2P_DEFAULT_OUTCOMING_QUOTA);
-        let mut dominant_blockstamp = Blockstamp::default();
-        let mut dominant_blockstamp_occurences = 0;
-        for (_ws2p_full_id, head) in self.heads_cache.clone() {
-            count_known_blockstamps += 1;
-            let blockstamps_occurences_copy = blockstamps_occurences.clone();
-            match blockstamps_occurences_copy.get(&head.blockstamp()) {
-                Some(occurences) => {
-                    let mut occurences_mut = blockstamps_occurences
-                        .get_mut(&head.blockstamp())
-                        .expect("WS2P: Fail to get_mut blockstamps_occurences !");
-                    *occurences_mut += 1;
-                    if *occurences > dominant_blockstamp_occurences {
-                        dominant_blockstamp_occurences = *occurences;
-                        dominant_blockstamp = head.blockstamp();
-                    }
-                }
-                None => {
-                    blockstamps_occurences.insert(head.blockstamp(), 0);
-                }
-            }
-            if head.blockstamp().id.0 > farthest_blockstamp.id.0 {
-                farthest_blockstamp = head.blockstamp();
-            }
-        }
-        if count_known_blockstamps < 5 {
-            return Err(NetworkConsensusError::InsufficientData(
-                count_known_blockstamps,
-            ));
-        } else if farthest_blockstamp == dominant_blockstamp {
-            return Ok(dominant_blockstamp);
-        }
-        Err(NetworkConsensusError::Fork)
-    }
-    fn count_established_connections(&self) -> usize {
-        let mut count_established_connections = 0;
-        for (_ws2p_full_id, (_ep, state)) in self.ws2p_endpoints.clone() {
-            if let WS2PConnectionState::Established = state {
-                count_established_connections += 1;
-            }
-        }
-        count_established_connections
-    }
-    pub fn connect_to_know_endpoints(&mut self) -> () {
-        let mut count_established_connections = 0;
-        let mut reachable_endpoints = Vec::new();
-        let mut unreachable_endpoints = Vec::new();
-        for (_ws2p_full_id, (ep, state)) in self.ws2p_endpoints.clone() {
-            match state {
-                WS2PConnectionState::Established => count_established_connections += 1,
-                WS2PConnectionState::NeverTry
-                | WS2PConnectionState::Close
-                | WS2PConnectionState::Denial => reachable_endpoints.push(ep),
-                _ => unreachable_endpoints.push(ep),
-            }
-        }
-        let mut free_outcoming_rooms = self
-            .conf
-            .clone()
-            .expect("WS2P: Fail to get conf !")
-            .outcoming_quota - count_established_connections;
-        while free_outcoming_rooms > 0 {
-            let ep = if !reachable_endpoints.is_empty() {
-                reachable_endpoints
-                    .pop()
-                    .expect("WS2P: Fail to pop() reachable_endpoints !")
-            } else if !unreachable_endpoints.is_empty() {
-                unreachable_endpoints
-                    .pop()
-                    .expect("WS2P: Fail to pop() unreachable_endpoints !")
-            } else {
-                break;
-            };
-            self.connect_to_without_checking_quotas(&ep);
-            free_outcoming_rooms -= 1;
-        }
-    }
-    pub fn connect_to(&mut self, endpoint: &NetworkEndpoint) -> () {
-        // Add endpoint to endpoints list (if there isn't already)
-        match self.ws2p_endpoints.get(
-            &endpoint
-                .node_full_id()
-                .expect("WS2P: Fail to get ep.node_full_id() !"),
-        ) {
-            Some(_) => {
-                self.ws2p_endpoints
-                    .get_mut(
-                        &endpoint
-                            .node_full_id()
-                            .expect("WS2P: Fail to get ep.node_full_id() !"),
-                    )
-                    .expect("WS2P: Fail to get_mut() a ws2p_endpoint !")
-                    .1 = WS2PConnectionState::NeverTry;
-            }
-            None => {
-                self.ws2p_endpoints.insert(
-                    endpoint
-                        .node_full_id()
-                        .expect("WS2P: Fail to get ep.node_full_id() !"),
-                    (endpoint.clone(), WS2PConnectionState::NeverTry),
-                );
-            }
-        };
-        if self
-            .conf
-            .clone()
-            .expect("WS2P: Fail to get conf !")
-            .outcoming_quota > self.count_established_connections()
-        {
-            self.connect_to_without_checking_quotas(&endpoint);
-        }
-    }
-    fn close_connection(&mut self, ws2p_full_id: &NodeFullId, reason: WS2PCloseConnectionReason) {
-        match reason {
-            WS2PCloseConnectionReason::NegociationTimeout => {}
-            WS2PCloseConnectionReason::AuthMessInvalidSig
-            | WS2PCloseConnectionReason::Timeout
-            | WS2PCloseConnectionReason::Unknow => {
-                self.ws2p_endpoints
-                    .get_mut(ws2p_full_id)
-                    .expect("Failure : attempt to delete a non-existent connection !")
-                    .1 = WS2PConnectionState::Close
-            }
-        }
-        self.connections_meta_datas.remove(ws2p_full_id);
-        self.websockets.remove(ws2p_full_id).expect(&format!(
-            "Fatal error : no websocket for {} !",
-            ws2p_full_id
-        ));
-        self.threads_senders_channels.remove(ws2p_full_id);
-    }
-    pub fn ws2p_conn_message_pretreatment(&mut self, message: WS2PConnectionMessage) -> WS2PSignal {
-        let connections_count = self.connections_meta_datas.len();
-        if connections_count == 0 {
-            return WS2PSignal::NoConnection;
-        }
-        let ws2p_full_id = message.0;
-        match message.1 {
-            WS2PConnectionMessagePayload::WrongUrl
-            | WS2PConnectionMessagePayload::FailOpenWS
-            | WS2PConnectionMessagePayload::FailToSplitWS => {
-                self.ws2p_endpoints
-                    .get_mut(&ws2p_full_id)
-                    .expect("WS2P: Fail to get mut ep !")
-                    .1 = WS2PConnectionState::WSError;
-                return WS2PSignal::WSError(ws2p_full_id);
-            }
-            WS2PConnectionMessagePayload::TryToSendConnectMess => {
-                self.ws2p_endpoints
-                    .get_mut(&ws2p_full_id)
-                    .expect("WS2P: Fail to get mut ep !")
-                    .1 = WS2PConnectionState::TryToSendConnectMess;
-            }
-            WS2PConnectionMessagePayload::FailSendConnectMess => {
-                self.ws2p_endpoints
-                    .get_mut(&ws2p_full_id)
-                    .expect("WS2P: Fail to mut ep !")
-                    .1 = WS2PConnectionState::Unreachable;
-            }
-            WS2PConnectionMessagePayload::WebsocketOk(sender) => {
-                self.websockets.insert(ws2p_full_id, sender);
-            }
-            WS2PConnectionMessagePayload::ValidConnectMessage(response, new_con_state) => {
-                self.ws2p_endpoints
-                    .get_mut(&ws2p_full_id)
-                    .expect("WS2P: Fail to get mut ep !")
-                    .1 = new_con_state;
-                if let WS2PConnectionState::ConnectMessOk = self.ws2p_endpoints[&ws2p_full_id].1 {
-                    trace!("Send: {:#?}", response);
-                    self.websockets
-                        .get_mut(&ws2p_full_id)
-                        .expect(&format!(
-                            "Fatal error : no websocket for {} !",
-                            ws2p_full_id
-                        ))
-                        .0
-                        .send_message(&Message::text(response))
-                        .expect("WS2P: Fail to send OK Message !");
-                }
-            }
-            WS2PConnectionMessagePayload::ValidAckMessage(r, new_con_state) => {
-                self.ws2p_endpoints
-                    .get_mut(&ws2p_full_id)
-                    .expect("WS2P: Fail to get mut ep !")
-                    .1 = new_con_state;
-                if let WS2PConnectionState::AckMessOk = self.ws2p_endpoints[&ws2p_full_id].1 {
-                    trace!("DEBUG : Send: {:#?}", r);
-                    self.websockets
-                        .get_mut(&ws2p_full_id)
-                        .expect(&format!(
-                            "Fatal error : no websocket for {} !",
-                            ws2p_full_id
-                        ))
-                        .0
-                        .send_message(&Message::text(r))
-                        .expect("WS2P: Fail to send Message in websocket !");
-                }
-            }
-            WS2PConnectionMessagePayload::ValidOk(new_con_state) => {
-                self.ws2p_endpoints
-                    .get_mut(&ws2p_full_id)
-                    .expect("WS2P: Fail to get mut ep !")
-                    .1 = new_con_state;
-                match self.ws2p_endpoints[&ws2p_full_id].1 {
-                    WS2PConnectionState::OkMessOkWaitingAckMess => {}
-                    WS2PConnectionState::Established => {
-                        return WS2PSignal::ConnectionEstablished(ws2p_full_id)
-                    }
-                    _ => {
-                        self.threads_senders_channels[&ws2p_full_id]
-                            .send(WS2POrderForListeningThread::Close)
-                            .expect("WS2P: Fail to send Close signel to connections threads !");
-                        self.close_connection(&ws2p_full_id, WS2PCloseConnectionReason::Unknow);
-                        return WS2PSignal::Empty;
-                    }
-                }
-            }
-            WS2PConnectionMessagePayload::DalRequest(req_id, req_body) => {
-                return WS2PSignal::DalRequest(ws2p_full_id, req_id, req_body);
-            }
-            WS2PConnectionMessagePayload::PeerCard(body, ws2p_endpoints) => {
-                return WS2PSignal::PeerCard(ws2p_full_id, body, ws2p_endpoints);
-            }
-            WS2PConnectionMessagePayload::Heads(heads) => {
-                let mut applied_heads = Vec::with_capacity(heads.len());
-                for head in heads {
-                    if let Some(head) = NetworkHead::from_json_value(&head) {
-                        if head.verify()
-                            && (self.my_head.is_none()
-                                || head.node_full_id()
-                                    != self
-                                        .my_head
-                                        .clone()
-                                        .expect("WS2P: Fail to clone my_head")
-                                        .node_full_id())
-                            && head.apply(&mut self.heads_cache)
-                        {
-                            applied_heads.push(head);
-                        }
-                    }
-                }
-                return WS2PSignal::Heads(ws2p_full_id, applied_heads);
-            }
-            WS2PConnectionMessagePayload::Document(network_doc) => {
-                return WS2PSignal::Document(ws2p_full_id, network_doc);
-            }
-            WS2PConnectionMessagePayload::ReqResponse(req_id, response) => {
-                if self.requests_awaiting_response.len() > req_id.0 as usize {
-                    if let Some((ref ws2p_request, ref recipient_fulld_id, ref _timestamp)) =
-                        self.requests_awaiting_response.remove(&req_id)
-                    {
-                        return WS2PSignal::ReqResponse(
-                            req_id,
-                            *ws2p_request,
-                            *recipient_fulld_id,
-                            response,
-                        );
-                    }
-                }
-            }
-            WS2PConnectionMessagePayload::NegociationTimeout => {
-                match self.ws2p_endpoints[&ws2p_full_id].1 {
-                    WS2PConnectionState::AckMessOk | WS2PConnectionState::ConnectMessOk => {
-                        self.ws2p_endpoints
-                            .get_mut(&ws2p_full_id)
-                            .expect("WS2P: Fail to get mut ep !")
-                            .1 = WS2PConnectionState::Denial
-                    }
-                    WS2PConnectionState::WaitingConnectMess => {
-                        self.ws2p_endpoints
-                            .get_mut(&ws2p_full_id)
-                            .expect("WS2P: Fail to get mut ep !")
-                            .1 = WS2PConnectionState::NoResponse
-                    }
-                    _ => {
-                        self.ws2p_endpoints
-                            .get_mut(&ws2p_full_id)
-                            .expect("WS2P: Fail to get mut ep !")
-                            .1 = WS2PConnectionState::Unreachable
-                    }
-                }
-                self.close_connection(&ws2p_full_id, WS2PCloseConnectionReason::NegociationTimeout);
-                return WS2PSignal::NegociationTimeout(ws2p_full_id);
-            }
-            WS2PConnectionMessagePayload::Timeout => {
-                self.close_connection(&ws2p_full_id, WS2PCloseConnectionReason::Timeout);
-                return WS2PSignal::Timeout(ws2p_full_id);
-            }
-            WS2PConnectionMessagePayload::UnknowMessage => warn!(
-                "WS2P : Receive Unknow Message from {}.",
-                &self.connections_meta_datas[&ws2p_full_id]
-                    .remote_pubkey
-                    .expect("WS2P: UnknowMessage : Fail to get remote_pubkey !")
-            ),
-            WS2PConnectionMessagePayload::WrongFormatMessage => warn!(
-                "WS2P : Receive Wrong Format Message from {}.",
-                &self.connections_meta_datas[&ws2p_full_id]
-                    .remote_pubkey
-                    .expect("WS2P: WrongFormatMessage : Fail to get remote_pubkey !")
-            ),
-            WS2PConnectionMessagePayload::InvalidMessage => return WS2PSignal::Empty,
-            WS2PConnectionMessagePayload::Close => {
-                self.close_connection(&ws2p_full_id, WS2PCloseConnectionReason::AuthMessInvalidSig)
-            }
-        }
-        // Detect timeout requests
-        let mut requests_timeout = Vec::new();
-        for &(ref req, ref _ws2p_full_id, ref timestamp) in
-            self.requests_awaiting_response.clone().values()
-        {
-            if SystemTime::now().duration_since(*timestamp).unwrap() > Duration::new(20, 0) {
-                requests_timeout.push(req.get_req_full_id());
-                warn!("request timeout : {:?}", req);
-            }
-        }
-        // Delete (and resend) timeout requests
-        for req_id in requests_timeout {
-            //let ws2p_endpoints = self.ws2p_endpoints.clone();
-            let _request_option = self.requests_awaiting_response.remove(&req_id.1);
-            /*if let Some((request, _, _)) = request_option {
-                let _request_result = self.send_request_to_specific_node(
-                    &get_random_connection(&ws2p_endpoints),
-                    &request,
-                );
-            }*/
-        }
-        WS2PSignal::Empty
-    }
-
-    pub fn send_request_to_all_connections(
-        &mut self,
-        ws2p_request: &NetworkRequest,
-    ) -> Result<(), SendRequestError> {
-        let mut count_successful_sending: usize = 0;
-        let mut errors: Vec<websocket::WebSocketError> = Vec::new();
-        match *ws2p_request {
-            NetworkRequest::GetCurrent(req_full_id, _receiver) => {
-                for (ws2p_full_id, (_ep, state)) in self.ws2p_endpoints.clone() {
-                    if let WS2PConnectionState::Established = state {
-                        let ws2p_request = NetworkRequest::GetCurrent(
-                            ModuleReqFullId(
-                                req_full_id.0,
-                                ModuleReqId(
-                                    (self.requests_awaiting_response.len()
-                                        + count_successful_sending)
-                                        as u32,
-                                ),
-                            ),
-                            ws2p_full_id,
-                        );
-                        match self.send_request_to_specific_node(&ws2p_full_id, &ws2p_request) {
-                            Ok(_) => count_successful_sending += 1,
-                            Err(e) => errors.push(e),
-                        };
-                    }
-                }
-            }
-            /* NetworkRequest::GetBlock(req_full_id, number) => {} */
-            NetworkRequest::GetBlocks(_req_full_id, _receiver, _count, _from_number) => {}
-            NetworkRequest::GetRequirementsPending(req_full_id, _receiver, min_cert) => {
-                for (ws2p_full_id, (_ep, state)) in self.ws2p_endpoints.clone() {
-                    if let WS2PConnectionState::Established = state {
-                        let ws2p_request = NetworkRequest::GetRequirementsPending(
-                            ModuleReqFullId(
-                                req_full_id.0,
-                                ModuleReqId(self.requests_awaiting_response.len() as u32),
-                            ),
-                            ws2p_full_id,
-                            min_cert,
-                        );
-                        match self.send_request_to_specific_node(&ws2p_full_id, &ws2p_request) {
-                            Ok(_) => count_successful_sending += 1,
-                            Err(e) => errors.push(e),
-                        };
-                    }
-                }
-            }
-            _ => {
-                return Err(SendRequestError::RequestTypeMustNotBeTransmitted());
-            }
-        }
-        debug!("count_successful_sending = {}", count_successful_sending);
-        if !errors.is_empty() {
-            return Err(SendRequestError::WSError(count_successful_sending, errors));
-        }
-        Ok(())
-    }
-
-    pub fn send_request_to_specific_node(
-        &mut self,
-        receiver_ws2p_full_id: &NodeFullId,
-        ws2p_request: &NetworkRequest,
-    ) -> Result<(), websocket::WebSocketError> {
-        self.websockets
-            .get_mut(receiver_ws2p_full_id)
-            .expect("WS2P: Fail to get mut websocket !")
-            .0
-            .send_message(&Message::text(
-                network_request_to_json(ws2p_request).to_string(),
-            ))?;
-        self.requests_awaiting_response.insert(
-            ws2p_request.get_req_id(),
-            (*ws2p_request, *receiver_ws2p_full_id, SystemTime::now()),
-        );
-        debug!(
-            "send request {} to {}",
-            network_request_to_json(ws2p_request).to_string(),
-            receiver_ws2p_full_id
-        );
-        Ok(())
-    }
-
-    fn connect_to_without_checking_quotas(&mut self, endpoint: &NetworkEndpoint) -> () {
-        // update connection state
-        self.ws2p_endpoints
-            .get_mut(
-                &endpoint
-                    .node_full_id()
-                    .expect("WS2P: Fail to get ep.node_full_id() !"),
-            )
-            .expect("Fatal error: try to connect to unlisted endpoint ! ")
-            .1 = WS2PConnectionState::TryToOpenWS;
-
-        // get endpoint url
-        let ws_url = endpoint.get_url(true);
-
-        // Create WS2PConnection
-        let mut conn_meta_datas = WS2PConnectionMetaData::new(
-            "b60a14fd-0826-4ae0-83eb-1a92cd59fd5308535fd3-78f2-4678-9315-cd6e3b7871b1".to_string(),
-        );
-        conn_meta_datas.remote_pubkey = Some(endpoint.pubkey());
-        conn_meta_datas.remote_uuid = Some(
-            endpoint
-                .node_uuid()
-                .expect("WS2P: Fail to get ep.node_uuid() !"),
-        );
-
-        // Prepare datas for listening thread
-        let mut datas_for_listening_thread = WS2PDatasForListeningThread {
-            conn_meta_datas: conn_meta_datas.clone(),
-            currency: self.currency.clone().expect("WS2P: Fail to get currency !"),
-            key_pair: self.key_pair.expect("WS2P: Fail to get key_pair!"),
-        };
-
-        // Create CONNECT Message
-        let mut connect_message = WS2PConnectMessageV1 {
-            currency: self.currency.clone().expect("WS2P: Fail to getcurrency !"),
-            pubkey: self
-                .key_pair
-                .expect("WS2P: Fail to get key_pair!")
-                .public_key(),
-            challenge: conn_meta_datas.challenge.clone(),
-            signature: None,
-        };
-        connect_message.signature =
-            Some(connect_message.sign(self.key_pair.expect("WS2P: Fail to get key_pair !")));
-        let json_connect_message =
-            serde_json::to_string(&connect_message).expect("Fail to serialize CONNECT message !");
-
-        // Log
-        trace!("Try connection to {} ...", ws_url);
-
-        // Listen incoming messages into a thread
-        let sender_to_main_thread: mpsc::Sender<WS2PThreadSignal> =
-            mpsc::Sender::clone(&self.main_thread_channel.0);
-        let (tx2, rx2) = mpsc::channel();
-        self.connections_meta_datas
-            .insert(conn_meta_datas.node_full_id(), conn_meta_datas.clone());
-        self.threads_senders_channels
-            .insert(conn_meta_datas.node_full_id(), tx2);
-        thread::spawn(move || {
-            // Open websocket
-            let open_ws_time = SystemTime::now();
-            let client = match ClientBuilder::new(&ws_url) {
-                Ok(mut client_builder) => match client_builder.connect_insecure() {
-                    Ok(c) => c,
-                    Err(_) => {
-                        debug!("WS2PConnectResult::FailOpenWS");
-                        sender_to_main_thread
-                            .send(WS2PThreadSignal::WS2PConnectionMessage(
-                                WS2PConnectionMessage(
-                                    datas_for_listening_thread.conn_meta_datas.node_full_id(),
-                                    WS2PConnectionMessagePayload::FailOpenWS,
-                                ),
-                            ))
-                            .unwrap_or(());
-                        return ();
-                    }
-                },
-                Err(_) => {
-                    warn!("WS2PConnectResult::WrongUrl : {}", ws_url);
-                    sender_to_main_thread
-                        .send(WS2PThreadSignal::WS2PConnectionMessage(
-                            WS2PConnectionMessage(
-                                datas_for_listening_thread.conn_meta_datas.node_full_id(),
-                                WS2PConnectionMessagePayload::WrongUrl,
-                            ),
-                        ))
-                        .unwrap_or(());
-
-                    return ();
-                }
-            };
-            let (mut receiver, mut sender) = match client.split() {
-                Ok((mut r, mut s)) => (r, s),
-                Err(_) => {
-                    sender_to_main_thread
-                        .send(WS2PThreadSignal::WS2PConnectionMessage(
-                            WS2PConnectionMessage(
-                                datas_for_listening_thread.conn_meta_datas.node_full_id(),
-                                WS2PConnectionMessagePayload::FailToSplitWS,
-                            ),
-                        ))
-                        .unwrap_or(());
-                    return ();
-                }
-            };
-
-            // Send CONNECT Message
-            sender_to_main_thread
-                .send(WS2PThreadSignal::WS2PConnectionMessage(
-                    WS2PConnectionMessage(
-                        datas_for_listening_thread.conn_meta_datas.node_full_id(),
-                        WS2PConnectionMessagePayload::TryToSendConnectMess,
-                    ),
-                ))
-                .unwrap_or(());
-            match sender.send_message(&Message::text(json_connect_message)) {
-                Ok(_) => {
-                    sender_to_main_thread
-                        .send(WS2PThreadSignal::WS2PConnectionMessage(
-                            WS2PConnectionMessage(
-                                datas_for_listening_thread.conn_meta_datas.node_full_id(),
-                                WS2PConnectionMessagePayload::WebsocketOk(WebsocketSender(sender)),
-                            ),
-                        ))
-                        .unwrap_or(());
-                }
-                Err(_) => {
-                    receiver.shutdown_all().unwrap_or(());
-                    sender_to_main_thread
-                        .send(WS2PThreadSignal::WS2PConnectionMessage(
-                            WS2PConnectionMessage(
-                                datas_for_listening_thread.conn_meta_datas.node_full_id(),
-                                WS2PConnectionMessagePayload::FailSendConnectMess,
-                            ),
-                        ))
-                        .unwrap_or(());
-                    return ();
-                }
-            }
-
-            let mut last_mess_time = SystemTime::now();
-            let mut spam_interval = false;
-            let mut spam_counter = 0;
-            for incoming_message in receiver.incoming_messages() {
-                // Spam ?
-                if SystemTime::now().duration_since(last_mess_time).unwrap()
-                    > Duration::new(*WS2P_SPAM_INTERVAL_IN_MILLI_SECS, 0)
-                {
-                    if spam_interval {
-                        spam_counter += 1;
-                    } else {
-                        spam_interval = true;
-                        spam_counter = 2;
-                    }
-                } else {
-                    spam_interval = false;
-                    spam_counter = 0;
-                }
-                // Spam ?
-                if spam_counter >= *WS2P_SPAM_LIMIT {
-                    thread::sleep(Duration::from_millis(*WS2P_SPAM_SLEEP_TIME_IN_SEC));
-                    last_mess_time = SystemTime::now();
-                } else {
-                    // Negociation timeout ?
-                    if datas_for_listening_thread.conn_meta_datas.state
-                        != WS2PConnectionState::Established
-                        && SystemTime::now().duration_since(open_ws_time).unwrap()
-                            > Duration::new(*WS2P_NEGOTIATION_TIMEOUT, 0)
-                    {
-                        sender_to_main_thread
-                            .send(WS2PThreadSignal::WS2PConnectionMessage(
-                                WS2PConnectionMessage(
-                                    datas_for_listening_thread.conn_meta_datas.node_full_id(),
-                                    WS2PConnectionMessagePayload::NegociationTimeout,
-                                ),
-                            ))
-                            .unwrap_or(());
-                        break;
-                    }
-                    // Connection timeout ?
-                    else if SystemTime::now().duration_since(last_mess_time).unwrap()
-                        > Duration::new(*WS2P_CONNECTION_TIMEOUT, 0)
-                    {
-                        sender_to_main_thread
-                            .send(WS2PThreadSignal::WS2PConnectionMessage(
-                                WS2PConnectionMessage(
-                                    datas_for_listening_thread.conn_meta_datas.node_full_id(),
-                                    WS2PConnectionMessagePayload::Timeout,
-                                ),
-                            ))
-                            .unwrap_or(());
-                        break;
-                    }
-                    last_mess_time = SystemTime::now();
-                    match rx2.recv_timeout(Duration::from_millis(40)) {
-                        Ok(s) => match s {
-                            WS2POrderForListeningThread::Close => break,
-                        },
-                        Err(e) => {
-                            match e {
-                                mpsc::RecvTimeoutError::Timeout => {
-                                    match incoming_message {
-                                        Ok(message) => {
-                                            if message.is_close() {
-                                                if sender_to_main_thread
-                                                    .send(WS2PThreadSignal::WS2PConnectionMessage(
-                                                        WS2PConnectionMessage(
-                                                            datas_for_listening_thread
-                                                                .conn_meta_datas
-                                                                .node_full_id(),
-                                                            WS2PConnectionMessagePayload::Close,
-                                                        ),
-                                                    ))
-                                                    .is_ok()
-                                                {
-                                                    break;
-                                                }
-                                            } else if message.is_data() {
-                                                // Parse message
-                                                let m = Message::from(message);
-                                                let s: String = from_utf8(&m.payload)
-                                                    .expect("WS2P: Fail to convert message payload to String !")
-                                                    .to_string();
-                                                let message: serde_json::Value =
-                                                    serde_json::from_str(&s)
-                                                    .expect("WS2P: Fail to convert string message ton json value !");
-                                                let result = sender_to_main_thread.send(
-                                                    WS2PThreadSignal::WS2PConnectionMessage(
-                                                        WS2PConnectionMessage(
-                                                            datas_for_listening_thread
-                                                                .conn_meta_datas
-                                                                .node_full_id(),
-                                                            datas_for_listening_thread
-                                                                .conn_meta_datas
-                                                                .parse_and_check_incoming_message(
-                                                                    &datas_for_listening_thread
-                                                                        .currency,
-                                                                    datas_for_listening_thread
-                                                                        .key_pair,
-                                                                    &message,
-                                                                ),
-                                                        ),
-                                                    ),
-                                                );
-                                                if result.is_err() {
-                                                    debug!("Close ws2p connection because ws2p main thread is unrechable !");
-                                                    break;
-                                                }
-                                            }
-                                        }
-                                        Err(e) => {
-                                            warn!("WebSocketError : {} ! Close ", e);
-                                            //receiver.shutdown_all().unwrap_or(());
-                                            break;
-                                        }
-                                    };
-                                }
-                                mpsc::RecvTimeoutError::Disconnected => {
-                                    break;
-                                }
-                            }
-                        }
-                    };
-                }
-            }
-        });
-    }
-}
-
 #[cfg(test)]
 mod tests {
     extern crate duniter_conf;
diff --git a/ws2p/ws2p_connection.rs b/ws2p/ws2p_connection.rs
index 84578ca8a48def9bb3084b73900b9fc828ea0a96..214ef3290051b81a56f39d3cc5cb787fc6ead58e 100644
--- a/ws2p/ws2p_connection.rs
+++ b/ws2p/ws2p_connection.rs
@@ -1,15 +1,194 @@
-extern crate serde_json;
-extern crate websocket;
-
+use constants::*;
 use duniter_crypto::keys::*;
 use duniter_module::ModuleReqId;
 use duniter_network::network_endpoint::{NetworkEndpoint, NetworkEndpointApi};
 use duniter_network::{NetworkDocument, NodeUUID};
 use parsers::blocks::parse_json_block;
-use std::fmt::Debug;
-use std::net::TcpStream;
+use rand::Rng;
+use std::sync::mpsc;
+#[allow(deprecated)]
+use ws::util::{Timeout, Token};
+use ws::{connect, CloseCode, Frame, Handler, Handshake, Message, Sender};
+use *;
+
+const CONNECT: Token = Token(1);
+const EXPIRE: Token = Token(2);
+
+/// Store a websocket sender
+pub struct WsSender(pub Sender);
+
+impl ::std::fmt::Debug for WsSender {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+        write!(f, "WsSender {{ }}")
+    }
+}
 
-use super::{NodeFullId, WS2PAckMessageV1, WS2PConnectMessageV1, WS2PMessage, WS2POkMessageV1};
+// Our Handler struct.
+// Here we explicity indicate that the Client needs a Sender,
+// whereas a closure captures the Sender for us automatically.
+#[allow(deprecated)]
+struct Client {
+    ws: Sender,
+    conductor_sender: mpsc::Sender<WS2PThreadSignal>,
+    currency: String,
+    key_pair: KeyPairEnum,
+    connect_message: Message,
+    conn_meta_datas: WS2PConnectionMetaDatas,
+    last_mess_time: SystemTime,
+    spam_interval: bool,
+    spam_counter: usize,
+    timeout: Option<Timeout>,
+}
+
+// We implement the Handler trait for Client so that we can get more
+// fine-grained control of the connection.
+impl Handler for Client {
+    // `on_open` will be called only after the WebSocket handshake is successful
+    // so at this point we know that the connection is ready to send/receive messages.
+    // We ignore the `Handshake` for now, but you could also use this method to setup
+    // Handler state or reject the connection based on the details of the Request
+    // or Response, such as by checking cookies or Auth headers.
+    fn on_open(&mut self, _: Handshake) -> ws::Result<()> {
+        // Define timeouts
+        self.ws.timeout(WS2P_NEGOTIATION_TIMEOUT * 1_000, CONNECT)?;
+        self.ws.timeout(WS2P_EXPIRE_TIMEOUT * 1_000, EXPIRE)?;
+        // Send ws::Sender to WS2PConductor
+        let result = self
+            .conductor_sender
+            .send(WS2PThreadSignal::WS2PConnectionMessage(
+                WS2PConnectionMessage(
+                    self.conn_meta_datas.node_full_id(),
+                    WS2PConnectionMessagePayload::WebsocketOk(WsSender(self.ws.clone())),
+                ),
+            ));
+        // If WS2PConductor is unrechable, close connection.
+        if result.is_err() {
+            debug!("Close ws2p connection because ws2p main thread is unrechable !");
+            self.ws.close(CloseCode::Normal)
+        } else {
+            // Send CONNECT Message
+            self.ws.send(self.connect_message.clone())
+        }
+    }
+
+    // `on_message` is roughly equivalent to the Handler closure. It takes a `Message`
+    // and returns a `Result<()>`.
+    fn on_message(&mut self, msg: Message) -> ws::Result<()> {
+        // Spam ?
+        if SystemTime::now()
+            .duration_since(self.last_mess_time)
+            .unwrap() > Duration::new(*WS2P_SPAM_INTERVAL_IN_MILLI_SECS, 0)
+        {
+            if self.spam_interval {
+                self.spam_counter += 1;
+            } else {
+                self.spam_interval = true;
+                self.spam_counter = 2;
+            }
+        } else {
+            self.spam_interval = false;
+            self.spam_counter = 0;
+        }
+        // Spam ?
+        if self.spam_counter >= *WS2P_SPAM_LIMIT {
+            thread::sleep(Duration::from_millis(*WS2P_SPAM_SLEEP_TIME_IN_SEC));
+            self.last_mess_time = SystemTime::now();
+            return Ok(());
+        }
+        self.last_mess_time = SystemTime::now();
+
+        // Parse and check incoming message
+        if msg.is_text() {
+            let s: String = msg
+                .into_text()
+                .expect("WS2P: Fail to convert message payload to String !");
+            trace!("WS2P: receive mess: {}", s);
+            let json_message: serde_json::Value = serde_json::from_str(&s)
+                .expect("WS2P: Fail to convert string message ton json value !");
+            let result = self
+                .conductor_sender
+                .send(WS2PThreadSignal::WS2PConnectionMessage(
+                    WS2PConnectionMessage(
+                        self.conn_meta_datas.node_full_id(),
+                        self.conn_meta_datas.parse_and_check_incoming_message(
+                            &self.currency,
+                            self.key_pair,
+                            &json_message,
+                        ),
+                    ),
+                ));
+            if result.is_err() {
+                info!("Close ws2p connection because ws2p main thread is unrechable !");
+                self.ws.close(CloseCode::Normal)?;
+            }
+        }
+        Ok(())
+    }
+    fn on_timeout(&mut self, event: Token) -> ws::Result<()> {
+        match event {
+            CONNECT => {
+                if self.conn_meta_datas.state != WS2PConnectionState::Established {
+                    let _result = self.conductor_sender.send(
+                        WS2PThreadSignal::WS2PConnectionMessage(WS2PConnectionMessage(
+                            self.conn_meta_datas.node_full_id(),
+                            WS2PConnectionMessagePayload::NegociationTimeout,
+                        )),
+                    );
+                    self.ws.close(CloseCode::Away)
+                } else {
+                    Ok(())
+                }
+            }
+            EXPIRE => {
+                let _result = self
+                    .conductor_sender
+                    .send(WS2PThreadSignal::WS2PConnectionMessage(
+                        WS2PConnectionMessage(
+                            self.conn_meta_datas.node_full_id(),
+                            WS2PConnectionMessagePayload::Timeout,
+                        ),
+                    ));
+                self.ws.close(CloseCode::Away)
+            }
+            _ => Ok(()),
+        }
+    }
+    #[allow(deprecated)]
+    fn on_new_timeout(&mut self, event: Token, timeout: Timeout) -> ws::Result<()> {
+        if event == EXPIRE {
+            if let Some(t) = self.timeout.take() {
+                self.ws.cancel(t)?;
+            }
+            self.timeout = Some(timeout)
+        }
+        Ok(())
+    }
+    fn on_frame(&mut self, frame: Frame) -> ws::Result<Option<Frame>> {
+        // some activity has occurred, let's reset the expiration timeout
+        self.ws.timeout(WS2P_EXPIRE_TIMEOUT * 1_000, EXPIRE)?;
+        Ok(Some(frame))
+    }
+    fn on_close(&mut self, code: CloseCode, reason: &str) {
+        // The WebSocket protocol allows for a utf8 reason for the closing state after the
+        // close code. WS-RS will attempt to interpret this data as a utf8 description of the
+        // reason for closing the connection. I many cases, `reason` will be an empty string.
+        // So, you may not normally want to display `reason` to the user,
+        // but let's assume that we know that `reason` is human-readable.
+        match code {
+            CloseCode::Normal => info!("The remote server close the connection."),
+            CloseCode::Away => info!("The remote server is leaving."),
+            _ => warn!("The remote server encountered an error: {}", reason),
+        }
+        let _result = self
+            .conductor_sender
+            .send(WS2PThreadSignal::WS2PConnectionMessage(
+                WS2PConnectionMessage(
+                    self.conn_meta_datas.node_full_id(),
+                    WS2PConnectionMessagePayload::Close,
+                ),
+            ));
+    }
+}
 
 #[derive(Debug, Copy, Clone)]
 pub enum WS2POrderForListeningThread {
@@ -74,14 +253,6 @@ impl WS2PConnectionState {
     }
 }
 
-pub struct WebsocketSender(pub websocket::sender::Writer<TcpStream>);
-
-impl Debug for WebsocketSender {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
-        write!(f, "WebsocketSender {{ }}")
-    }
-}
-
 #[derive(Debug)]
 pub enum WS2PConnectionMessagePayload {
     FailOpenWS,
@@ -89,7 +260,7 @@ pub enum WS2PConnectionMessagePayload {
     FailToSplitWS,
     TryToSendConnectMess,
     FailSendConnectMess,
-    WebsocketOk(WebsocketSender),
+    WebsocketOk(WsSender),
     NegociationTimeout,
     ValidConnectMessage(String, WS2PConnectionState),
     ValidAckMessage(String, WS2PConnectionState),
@@ -118,7 +289,7 @@ pub enum WS2PCloseConnectionReason {
 }
 
 #[derive(Debug, Clone)]
-pub struct WS2PConnectionMetaData {
+pub struct WS2PConnectionMetaDatas {
     pub state: WS2PConnectionState,
     pub remote_uuid: Option<NodeUUID>,
     pub remote_pubkey: Option<PubKey>,
@@ -129,14 +300,14 @@ pub struct WS2PConnectionMetaData {
 
 #[derive(Debug, Clone)]
 pub struct WS2PDatasForListeningThread {
-    pub conn_meta_datas: WS2PConnectionMetaData,
+    pub conn_meta_datas: WS2PConnectionMetaDatas,
     pub currency: String,
     pub key_pair: KeyPairEnum,
 }
 
-impl WS2PConnectionMetaData {
+impl WS2PConnectionMetaDatas {
     pub fn new(challenge: String) -> Self {
-        WS2PConnectionMetaData {
+        WS2PConnectionMetaDatas {
             state: WS2PConnectionState::WaitingConnectMess,
             remote_uuid: None,
             remote_pubkey: None,
@@ -170,7 +341,7 @@ impl WS2PConnectionMetaData {
                         if message.verify() && message.pubkey == self.remote_pubkey.unwrap() {
                             match self.state {
                                 WS2PConnectionState::WaitingConnectMess => {
-                                    trace!("CONNECT sig is valid.");
+                                    debug!("CONNECT sig is valid.");
                                     self.state = WS2PConnectionState::ConnectMessOk;
                                     self.remote_challenge = message.challenge.clone();
                                     let mut response = WS2PAckMessageV1 {
@@ -397,3 +568,83 @@ impl WS2PConnectionMetaData {
         }
     }
 }
+
+pub fn get_random_connection<S: ::std::hash::BuildHasher>(
+    connections: &HashMap<NodeFullId, (NetworkEndpoint, WS2PConnectionState), S>,
+) -> NodeFullId {
+    let mut rng = rand::thread_rng();
+    let mut loop_count = 0;
+    loop {
+        for (ws2p_full_id, (_ep, state)) in &(*connections) {
+            if loop_count > 10 {
+                return *ws2p_full_id;
+            }
+            if let WS2PConnectionState::Established = state {
+                if rng.gen::<bool>() {
+                    return *ws2p_full_id;
+                }
+            }
+        }
+        loop_count += 1;
+    }
+}
+
+pub fn connect_to_ws2p_endpoint(
+    endpoint: &NetworkEndpoint,
+    conductor_sender: &mpsc::Sender<WS2PThreadSignal>,
+    currency: &str,
+    key_pair: KeyPairEnum,
+) -> ws::Result<()> {
+    // Get endpoint url
+    let ws_url = endpoint.get_url(true);
+
+    // Create WS2PConnectionMetaDatass
+    let mut conn_meta_datas = WS2PConnectionMetaDatas::new(
+        "b60a14fd-0826-4ae0-83eb-1a92cd59fd5308535fd3-78f2-4678-9315-cd6e3b7871b1".to_string(),
+    );
+    conn_meta_datas.remote_pubkey = Some(endpoint.pubkey());
+    conn_meta_datas.remote_uuid = Some(
+        endpoint
+            .node_uuid()
+            .expect("WS2P: Fail to get ep.node_uuid() !"),
+    );
+
+    // Generate connect message
+    let connect_message =
+        generate_connect_message(currency, key_pair, conn_meta_datas.challenge.clone());
+
+    // Log
+    info!("Try connection to {} ...", ws_url);
+
+    // Connect to websocket
+    connect(ws_url, |ws| Client {
+        ws,
+        conductor_sender: conductor_sender.clone(),
+        currency: String::from(currency),
+        key_pair,
+        connect_message: connect_message.clone(),
+        conn_meta_datas: conn_meta_datas.clone(),
+        last_mess_time: SystemTime::now(),
+        spam_interval: false,
+        spam_counter: 0,
+        timeout: None,
+    })
+}
+
+pub fn generate_connect_message(
+    currency: &str,
+    key_pair: KeyPairEnum,
+    challenge: String,
+) -> Message {
+    // Create CONNECT Message
+    let mut connect_message = WS2PConnectMessageV1 {
+        currency: String::from(currency),
+        pubkey: key_pair.public_key(),
+        challenge,
+        signature: None,
+    };
+    connect_message.signature = Some(connect_message.sign(key_pair));
+    Message::text(
+        serde_json::to_string(&connect_message).expect("Fail to serialize CONNECT message !"),
+    )
+}
diff --git a/ws2p/ws2p_db.rs b/ws2p/ws2p_db.rs
index 5ffad9a7d8627903ef91d41e5c503b987a38cdc8..5d34ff28aeb62a6c6bf5f08f3df319972295497b 100644
--- a/ws2p/ws2p_db.rs
+++ b/ws2p/ws2p_db.rs
@@ -5,7 +5,6 @@ extern crate duniter_module;
 extern crate duniter_network;
 extern crate serde_json;
 extern crate sqlite;
-extern crate websocket;
 
 use duniter_crypto::keys::*;
 use duniter_network::network_endpoint::{NetworkEndpoint, NetworkEndpointApi};