diff --git a/network/src/client.rs b/network/src/client.rs
new file mode 100644
index 0000000000..217e81afd9
--- /dev/null
+++ b/network/src/client.rs
@@ -0,0 +1,133 @@
+// Copyright 2018 Kodebox, Inc.
+// This file is part of CodeChain.
+//
+// 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 .
+
+use std::collections::HashMap;
+use std::sync::{Arc, Weak};
+
+use mio::Token;
+use parking_lot::RwLock;
+
+use super::Extension;
+
+#[derive(Clone, Eq, PartialEq)]
+pub struct NodeId(Token);
+
+pub enum Error {
+ DuplicatedApplicationName,
+ ExtensionDropped,
+}
+
+pub struct ClientApi {
+ client: Weak,
+ extension: Weak,
+}
+
+impl ClientApi {
+ fn send_async(&self, id: &NodeId, message: &Vec) {
+ if let Some(extension) = self.extension.upgrade() {
+ let _need_encryption = extension.need_encryption();
+ info!("send {:?} to {:?}", message, id.0);
+ } else {
+ info!("The extension already dropped");
+ }
+ }
+
+ fn send_sync(&self, id: &NodeId, message: &Vec) -> Result<(), Error> {
+ if let Some(extension) = self.extension.upgrade() {
+ let _need_encryption = extension.need_encryption();
+ info!("send {:?} to {:?}", message, id.0);
+ Ok(())
+ } else {
+ info!("The extension already dropped");
+ Err(Error::ExtensionDropped)
+ }
+ }
+
+ fn connect_sync(&self, id: &NodeId) -> Result<(), Error> {
+ if let Some(extension) = self.extension.upgrade() {
+ let _need_encryption = extension.need_encryption();
+ info!("connect_async to {:?}", id.0);
+ Ok(())
+ } else {
+ info!("The extension already dropped");
+ Err(Error::ExtensionDropped)
+ }
+ }
+}
+
+struct Client {
+ extensions: RwLock>>,
+}
+
+impl Client {
+ pub fn register_extension(client: Arc, extension: Arc) -> Result<(), Error> {
+ let name = extension.name();
+ let mut extensions = client.extensions.write();
+ if extensions.contains_key(&name) {
+ return Err(Error::DuplicatedApplicationName)
+ }
+ let api = Arc::new(ClientApi {
+ client: Arc::downgrade(&client),
+ extension: Arc::downgrade(&extension),
+ });
+ if let Some(_) = extensions.insert(name, Arc::downgrade(&extension)) {
+ unreachable!();
+ }
+ extension.on_initialize(api);
+ Ok(())
+ }
+
+ fn new() -> Self {
+ Self {
+ extensions: RwLock::new(HashMap::new()),
+ }
+ }
+
+ fn on_message(&self, name: &String, id: &NodeId, data: &Vec) {
+ let extensions = self.extensions.read();
+ if let Some(extension) = extensions.get(name) {
+ if let Some(extension) = extension.upgrade() {
+ extension.on_message(&id, data);
+ } else {
+ info!("The extension already dropped");
+ }
+ } else {
+ info!("The handler for {} doesn't exist.", name);
+ }
+ }
+
+ fn on_node_added(&self, id: &NodeId) {
+ let extensions = self.extensions.read();
+ for (_, extension) in extensions.iter() {
+ if let Some(extension) = extension.upgrade() {
+ extension.on_node_added(&id);
+ } else {
+ info!("ClientApi already dropped");
+ }
+ }
+ }
+
+ fn on_node_removed(&self, id: &NodeId) {
+ let extensions = self.extensions.read();
+ for (_, extension) in extensions.iter() {
+ if let Some(extension) = extension.upgrade() {
+ extension.on_node_removed(&id);
+ } else {
+ info!("The extension already dropped");
+ }
+ }
+ }
+}
diff --git a/network/src/extension.rs b/network/src/extension.rs
new file mode 100644
index 0000000000..5370e4d76b
--- /dev/null
+++ b/network/src/extension.rs
@@ -0,0 +1,39 @@
+// Copyright 2018 Kodebox, Inc.
+// This file is part of CodeChain.
+//
+// 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 .
+
+use std::sync::Arc;
+
+use super::ClientApi;
+use super::Error;
+use super::NodeId;
+
+pub trait Extension {
+ fn name(&self) -> String;
+ fn need_encryption(&self) -> bool;
+
+ fn on_initialize(&self, api: Arc);
+
+ fn on_node_added(&self, id: &NodeId);
+ fn on_node_removed(&self, id: &NodeId);
+
+ fn on_connected(&self, id: &NodeId);
+ fn on_connection_allowed(&self, id: &NodeId);
+ fn on_connection_denied(&self, id: &NodeId, error: Error);
+
+ fn on_message(&self, id: &NodeId, message: &Vec);
+
+ fn on_close(&self);
+}
diff --git a/network/src/lib.rs b/network/src/lib.rs
index 716556b1d6..d5c8932c63 100644
--- a/network/src/lib.rs
+++ b/network/src/lib.rs
@@ -26,11 +26,15 @@ extern crate codechain_crypto as ccrypto;
extern crate codechain_io as cio;
extern crate codechain_types as ctypes;
+mod client;
mod connection;
+mod extension;
mod handshake;
pub mod session;
pub mod address;
pub mod kademlia;
-pub use address::Address;
-pub use handshake::HandshakeService;
+pub use self::address::Address;
+pub use self::client::{ClientApi, Error, NodeId};
+pub use self::extension::Extension;
+pub use self::handshake::HandshakeService;