diff --git a/HISTORY.md b/HISTORY.md
index 88d5db59..f7f25ff3 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -37,6 +37,7 @@ Changes:
 - Remove `context.RenderTemplateSource` you should make a new template file and use the `iris.Render` to specify an `io.Writer` like `bytes.Buffer`
 - Remove  `plugins`, replaced with more pluggable echosystem that I designed from zero on this release, named `Policy` [Adaptors](https://github.com/kataras/iris/tree/master/adaptors) (all plugins have been converted, fixed and improvement, except the iriscontrol).
 - `context.Log(string,...interface{})` -> `context.Log(iris.LogMode, string)`
+- Remove `.Config.Websocket` , replaced with the `kataras/iris/adaptors/websocket.Config` adaptor.
 
 - https://github.com/iris-contrib/plugin      ->  https://github.com/iris-contrib/adaptors
 
@@ -859,18 +860,208 @@ editors worked before but I couldn't let some developers without support.
 
 ### Websockets
 
-There are many internal improvements to the [websocket server](https://github.com/kataras/go-websocket), and it's
-operating slighty faster.
+There are many internal improvements to the websocket server, it
+operates slighty faster to.
 
-The kataras/go-websocket library, which `app.OnConnection` is refering to, will not be changed, its API will still remain.
-I am not putting anything new there (I doubt if any bug is available to fix, it's very simple and it just works).
 
-I started the kataras/go-websocket back then because I wanted a simple and fast websocket server for
-the fasthttp iris' version and back then no one did that before.
-Now(after v6) iris is compatible with any net/http websocket library that already created by third-parties.
+Websocket is an Adaptor too and you can edit more configuration fields than before.
+No Write and Read timeout by default, you have to set the fields if you want to enable timeout.
 
-If the iris' websocket feature does not cover your app's needs, you can simple use any other
-library for websockets, like the Golang's compatible to `socket.io`, example:
+Below you'll see the before and the after, keep note that the static and templates didn't changed, so I am not putting the whole
+html and javascript sources here, you can run the full examples from [here](https://github.com/kataras/iris/tree/6.2/adaptors/websocket/_examples).
+
+**BEFORE:***
+
+```go
+
+package main
+
+import (
+	"fmt" // optional
+
+	"github.com/kataras/iris"
+)
+
+type clientPage struct {
+	Title string
+	Host  string
+}
+
+func main() {
+	iris.StaticWeb("/js", "./static/js")
+
+	iris.Get("/", func(ctx *iris.Context) {
+		ctx.Render("client.html", clientPage{"Client Page", ctx.Host()})
+	})
+
+	// the path which the websocket client should listen/registed to ->
+	iris.Config.Websocket.Endpoint = "/my_endpoint"
+	// by-default all origins are accepted, you can change this behavior by setting:
+	// iris.Config.Websocket.CheckOrigin
+
+	var myChatRoom = "room1"
+	iris.Websocket.OnConnection(func(c iris.WebsocketConnection) {
+		// Request returns the (upgraded) *http.Request of this connection
+		// avoid using it, you normally don't need it,
+		// websocket has everything you need to authenticate the user BUT if it's necessary
+		// then  you use it to receive user information, for example: from headers.
+
+		// httpRequest := c.Request()
+		// fmt.Printf("Headers for the connection with ID: %s\n\n", c.ID())
+		// for k, v := range httpRequest.Header {
+		// fmt.Printf("%s = '%s'\n", k, strings.Join(v, ", "))
+		// }
+
+		// join to a room (optional)
+		c.Join(myChatRoom)
+
+		c.On("chat", func(message string) {
+			if message == "leave" {
+				c.Leave(myChatRoom)
+				c.To(myChatRoom).Emit("chat", "Client with ID: "+c.ID()+" left from the room and cannot send or receive message to/from this room.")
+				c.Emit("chat", "You have left from the room: "+myChatRoom+" you cannot send or receive any messages from others inside that room.")
+				return
+			}
+			// to all except this connection ->
+			// c.To(iris.Broadcast).Emit("chat", "Message from: "+c.ID()+"-> "+message)
+			// to all connected clients: c.To(iris.All)
+
+			// to the client itself ->
+			//c.Emit("chat", "Message from myself: "+message)
+
+			//send the message to the whole room,
+			//all connections are inside this room will receive this message
+			c.To(myChatRoom).Emit("chat", "From: "+c.ID()+": "+message)
+		})
+
+		// or create a new leave event
+		// c.On("leave", func() {
+		// 	c.Leave(myChatRoom)
+		// })
+
+		c.OnDisconnect(func() {
+			fmt.Printf("Connection with ID: %s has been disconnected!\n", c.ID())
+
+		})
+	})
+
+	iris.Listen(":8080")
+}
+
+
+
+```
+
+
+**AFTER**
+```go
+package main
+
+import (
+	"fmt" // optional
+
+	"gopkg.in/kataras/iris.v6"
+	"gopkg.in/kataras/iris.v6/adaptors/httprouter"
+	"gopkg.in/kataras/iris.v6/adaptors/view"
+	"gopkg.in/kataras/iris.v6/adaptors/websocket"
+)
+
+type clientPage struct {
+	Title string
+	Host  string
+}
+
+func main() {
+	app := iris.New()
+	app.Adapt(iris.DevLogger())                  // enable all (error) logs
+	app.Adapt(httprouter.New())                  // select the httprouter as the servemux
+	app.Adapt(view.HTML("./templates", ".html")) // select the html engine to serve templates
+
+	ws := websocket.New(websocket.Config{
+		// the path which the websocket client should listen/registed to,
+		Endpoint: "/my_endpoint",
+		// the client-side javascript static file path
+		// which will be served by Iris.
+		// default is /iris-ws.js
+		// if you change that you have to change the bottom of templates/client.html
+		// script tag:
+		ClientSourcePath: "/iris-ws.js",
+		//
+		// Set the timeouts, 0 means no timeout
+		// websocket has more configuration, go to ../../config.go for more:
+		// WriteTimeout: 0,
+		// ReadTimeout:  0,
+		// by-default all origins are accepted, you can change this behavior by setting:
+		// CheckOrigin: (r *http.Request ) bool {},
+		//
+		//
+		// IDGenerator used to create (and later on, set)
+		// an ID for each incoming websocket connections (clients).
+		// The request is an argument which you can use to generate the ID (from headers for example).
+		// If empty then the ID is generated by DefaultIDGenerator: randomString(64):
+		// IDGenerator func(ctx *iris.Context) string {},
+	})
+
+	app.Adapt(ws) // adapt the websocket server, you can adapt more than one with different Endpoint
+
+	app.StaticWeb("/js", "./static/js") // serve our custom javascript code
+
+	app.Get("/", func(ctx *iris.Context) {
+		ctx.Render("client.html", clientPage{"Client Page", ctx.Host()})
+	})
+
+	var myChatRoom = "room1"
+
+	ws.OnConnection(func(c websocket.Connection) {
+		// Context returns the (upgraded) *iris.Context of this connection
+		// avoid using it, you normally don't need it,
+		// websocket has everything you need to authenticate the user BUT if it's necessary
+		// then  you use it to receive user information, for example: from headers.
+
+		// ctx := c.Context()
+
+		// join to a room (optional)
+		c.Join(myChatRoom)
+
+		c.On("chat", func(message string) {
+			if message == "leave" {
+				c.Leave(myChatRoom)
+				c.To(myChatRoom).Emit("chat", "Client with ID: "+c.ID()+" left from the room and cannot send or receive message to/from this room.")
+				c.Emit("chat", "You have left from the room: "+myChatRoom+" you cannot send or receive any messages from others inside that room.")
+				return
+			}
+			// to all except this connection ->
+			// c.To(websocket.Broadcast).Emit("chat", "Message from: "+c.ID()+"-> "+message)
+			// to all connected clients: c.To(websocket.All)
+
+			// to the client itself ->
+			//c.Emit("chat", "Message from myself: "+message)
+
+			//send the message to the whole room,
+			//all connections are inside this room will receive this message
+			c.To(myChatRoom).Emit("chat", "From: "+c.ID()+": "+message)
+		})
+
+		// or create a new leave event
+		// c.On("leave", func() {
+		// 	c.Leave(myChatRoom)
+		// })
+
+		c.OnDisconnect(func() {
+			fmt.Printf("Connection with ID: %s has been disconnected!\n", c.ID())
+		})
+	})
+
+	app.Listen(":8080")
+}
+
+```
+
+
+
+
+If the iris' websocket feature does not cover your app's needs, you can simply use any other
+library for websockets that you used to use, like the Golang's compatible to `socket.io`, simple example:
 
 ```go
 package main
diff --git a/adaptors/httprouter/httprouter.go b/adaptors/httprouter/httprouter.go
index 11802ee3..bb030490 100644
--- a/adaptors/httprouter/httprouter.go
+++ b/adaptors/httprouter/httprouter.go
@@ -68,30 +68,30 @@ type (
 
 var (
 	errMuxEntryConflictsWildcard = errors.New(`
-			Router: '%s' in new path '%s'
+			httprouter: '%s' in new path '%s'
 							conflicts with existing wildcarded route with path: '%s'
 							in existing prefix of'%s' `)
 
 	errMuxEntryMiddlewareAlreadyExists = errors.New(`
-		Router: Middleware were already registered for the path: '%s'`)
+		httprouter: Middleware were already registered for the path: '%s'`)
 
 	errMuxEntryInvalidWildcard = errors.New(`
-		Router: More than one wildcard found in the path part: '%s' in route's path: '%s'`)
+		httprouter: More than one wildcard found in the path part: '%s' in route's path: '%s'`)
 
 	errMuxEntryConflictsExistingWildcard = errors.New(`
-		Router: Wildcard for route path: '%s' conflicts with existing children in route path: '%s'`)
+		httprouter: Wildcard for route path: '%s' conflicts with existing children in route path: '%s'`)
 
 	errMuxEntryWildcardUnnamed = errors.New(`
-		Router: Unnamed wildcard found in path: '%s'`)
+		httprouter: Unnamed wildcard found in path: '%s'`)
 
 	errMuxEntryWildcardInvalidPlace = errors.New(`
-		Router: Wildcard is only allowed at the end of the path, in the route path: '%s'`)
+		httprouter: Wildcard is only allowed at the end of the path, in the route path: '%s'`)
 
 	errMuxEntryWildcardConflictsMiddleware = errors.New(`
-		Router: Wildcard  conflicts with existing middleware for the route path: '%s'`)
+		httprouter: Wildcard  conflicts with existing middleware for the route path: '%s'`)
 
 	errMuxEntryWildcardMissingSlash = errors.New(`
-		Router: No slash(/) were found before wildcard in the route path: '%s'`)
+		httprouter: No slash(/) were found before wildcard in the route path: '%s'`)
 )
 
 // getParamsLen returns the parameters length from a given path
@@ -575,7 +575,7 @@ func New() iris.Policies {
 					// while ProdMode means that the iris should not continue running
 					// by-default it panics on these errors, but to make sure let's introduce the fatalErr to stop visiting
 					fatalErr = true
-					logger(iris.ProdMode, "fatal error on  httprouter build adaptor: "+err.Error())
+					logger(iris.ProdMode, err.Error())
 					return
 				}
 
diff --git a/adaptors/view/LICENSE b/adaptors/view/LICENSE
new file mode 100644
index 00000000..2935ad5d
--- /dev/null
+++ b/adaptors/view/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016-2017 Gerasimos Maropoulos
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/adaptors/websocket/LICENSE b/adaptors/websocket/LICENSE
new file mode 100644
index 00000000..cbd1a2ca
--- /dev/null
+++ b/adaptors/websocket/LICENSE
@@ -0,0 +1,44 @@
+The MIT License (MIT)
+
+Copyright (c) 2016-2017 Gerasimos Maropoulos
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+  Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/adaptors/websocket/_examples/websocket/main.go b/adaptors/websocket/_examples/websocket/main.go
new file mode 100644
index 00000000..be69bb08
--- /dev/null
+++ b/adaptors/websocket/_examples/websocket/main.go
@@ -0,0 +1,99 @@
+package main
+
+import (
+	"fmt" // optional
+
+	"gopkg.in/kataras/iris.v6"
+	"gopkg.in/kataras/iris.v6/adaptors/httprouter"
+	"gopkg.in/kataras/iris.v6/adaptors/view"
+	"gopkg.in/kataras/iris.v6/adaptors/websocket"
+)
+
+type clientPage struct {
+	Title string
+	Host  string
+}
+
+func main() {
+	app := iris.New()
+	app.Adapt(iris.DevLogger())                  // enable all (error) logs
+	app.Adapt(httprouter.New())                  // select the httprouter as the servemux
+	app.Adapt(view.HTML("./templates", ".html")) // select the html engine to serve templates
+
+	ws := websocket.New(websocket.Config{
+		// the path which the websocket client should listen/registed to,
+		Endpoint: "/my_endpoint",
+		// the client-side javascript static file path
+		// which will be served by Iris.
+		// default is /iris-ws.js
+		// if you change that you have to change the bottom of templates/client.html
+		// script tag:
+		ClientSourcePath: "/iris-ws.js",
+		//
+		// Set the timeouts, 0 means no timeout
+		// websocket has more configuration, go to ../../config.go for more:
+		// WriteTimeout: 0,
+		// ReadTimeout:  0,
+		// by-default all origins are accepted, you can change this behavior by setting:
+		// CheckOrigin: (r *http.Request ) bool {},
+		//
+		//
+		// IDGenerator used to create (and later on, set)
+		// an ID for each incoming websocket connections (clients).
+		// The request is an argument which you can use to generate the ID (from headers for example).
+		// If empty then the ID is generated by DefaultIDGenerator: randomString(64):
+		// IDGenerator func(ctx *iris.Context) string {},
+	})
+
+	app.Adapt(ws) // adapt the websocket server, you can adapt more than one with different Endpoint
+
+	app.StaticWeb("/js", "./static/js") // serve our custom javascript code
+
+	app.Get("/", func(ctx *iris.Context) {
+		ctx.Render("client.html", clientPage{"Client Page", ctx.Host()})
+	})
+
+	var myChatRoom = "room1"
+
+	ws.OnConnection(func(c websocket.Connection) {
+		// Context returns the (upgraded) *iris.Context of this connection
+		// avoid using it, you normally don't need it,
+		// websocket has everything you need to authenticate the user BUT if it's necessary
+		// then  you use it to receive user information, for example: from headers.
+
+		// ctx := c.Context()
+
+		// join to a room (optional)
+		c.Join(myChatRoom)
+
+		c.On("chat", func(message string) {
+			if message == "leave" {
+				c.Leave(myChatRoom)
+				c.To(myChatRoom).Emit("chat", "Client with ID: "+c.ID()+" left from the room and cannot send or receive message to/from this room.")
+				c.Emit("chat", "You have left from the room: "+myChatRoom+" you cannot send or receive any messages from others inside that room.")
+				return
+			}
+			// to all except this connection ->
+			// c.To(websocket.Broadcast).Emit("chat", "Message from: "+c.ID()+"-> "+message)
+			// to all connected clients: c.To(websocket.All)
+
+			// to the client itself ->
+			//c.Emit("chat", "Message from myself: "+message)
+
+			//send the message to the whole room,
+			//all connections are inside this room will receive this message
+			c.To(myChatRoom).Emit("chat", "From: "+c.ID()+": "+message)
+		})
+
+		// or create a new leave event
+		// c.On("leave", func() {
+		// 	c.Leave(myChatRoom)
+		// })
+
+		c.OnDisconnect(func() {
+			fmt.Printf("Connection with ID: %s has been disconnected!\n", c.ID())
+		})
+	})
+
+	app.Listen(":8080")
+}
diff --git a/adaptors/websocket/_examples/websocket/static/js/chat.js b/adaptors/websocket/_examples/websocket/static/js/chat.js
new file mode 100644
index 00000000..920a2050
--- /dev/null
+++ b/adaptors/websocket/_examples/websocket/static/js/chat.js
@@ -0,0 +1,38 @@
+var messageTxt;
+var messages;
+
+$(function () {
+
+	messageTxt = $("#messageTxt");
+	messages = $("#messages");
+
+
+	w = new Ws("ws://" + HOST + "/my_endpoint");
+	w.OnConnect(function () {
+		console.log("Websocket connection established");
+	});
+
+	w.OnDisconnect(function () {
+		appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
+	});
+
+	w.On("chat", function (message) {
+		appendMessage($("<div>" + message + "</div>"));
+	});
+
+	$("#sendBtn").click(function () {
+		w.Emit("chat", messageTxt.val().toString());
+		messageTxt.val("");
+	});
+
+})
+
+
+function appendMessage(messageDiv) {
+    var theDiv = messages[0];
+    var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
+    messageDiv.appendTo(messages);
+    if (doScroll) {
+        theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
+    }
+}
diff --git a/adaptors/websocket/_examples/websocket/templates/client.html b/adaptors/websocket/_examples/websocket/templates/client.html
new file mode 100644
index 00000000..04ad2115
--- /dev/null
+++ b/adaptors/websocket/_examples/websocket/templates/client.html
@@ -0,0 +1,24 @@
+<html>
+
+<head>
+<title>{{ .Title}}</title>
+</head>
+
+<body>
+	<div id="messages"
+		style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
+
+	</div>
+	<input type="text" id="messageTxt" />
+	<button type="button" id="sendBtn">Send</button>
+	<script type="text/javascript">
+		var HOST = {{.Host}}
+	</script>
+	<script src="js/vendor/jquery-2.2.3.min.js" type="text/javascript"></script>
+	<!-- This is auto-serving by the Iris, you don't need to have this file in your disk-->
+	<script src="/iris-ws.js" type="text/javascript"></script>
+	<!-- -->
+	<script src="js/chat.js" type="text/javascript"></script>
+</body>
+
+</html>
diff --git a/adaptors/websocket/_examples/websocket_connectionlist/main.go b/adaptors/websocket/_examples/websocket_connectionlist/main.go
new file mode 100644
index 00000000..1127a944
--- /dev/null
+++ b/adaptors/websocket/_examples/websocket_connectionlist/main.go
@@ -0,0 +1,113 @@
+package main
+
+import (
+	"fmt"
+	"sync"
+	"time"
+
+	"gopkg.in/kataras/iris.v6"
+	"gopkg.in/kataras/iris.v6/adaptors/httprouter"
+	"gopkg.in/kataras/iris.v6/adaptors/view"
+	"gopkg.in/kataras/iris.v6/adaptors/websocket"
+)
+
+type clientPage struct {
+	Title string
+	Host  string
+}
+
+func main() {
+	app := iris.New()
+	app.Adapt(iris.DevLogger())                  // enable all (error) logs
+	app.Adapt(httprouter.New())                  // select the httprouter as the servemux
+	app.Adapt(view.HTML("./templates", ".html")) // select the html engine to serve templates
+
+	ws := websocket.New(websocket.Config{
+		// the path which the websocket client should listen/registed to,
+		Endpoint: "/my_endpoint",
+		// the client-side javascript static file path
+		// which will be served by Iris.
+		// default is /iris-ws.js
+		// if you change that you have to change the bottom of templates/client.html
+		// script tag:
+		ClientSourcePath: "/iris-ws.js",
+		//
+		// Set the timeouts, 0 means no timeout
+		// websocket has more configuration, go to ../../config.go for more:
+		// WriteTimeout: 0,
+		// ReadTimeout:  0,
+		// by-default all origins are accepted, you can change this behavior by setting:
+		// CheckOrigin: (r *http.Request ) bool {},
+		//
+		//
+		// IDGenerator used to create (and later on, set)
+		// an ID for each incoming websocket connections (clients).
+		// The request is an argument which you can use to generate the ID (from headers for example).
+		// If empty then the ID is generated by DefaultIDGenerator: randomString(64):
+		// IDGenerator func(ctx *iris.Context) string {},
+	})
+
+	app.Adapt(ws) // adapt the websocket server, you can adapt more than one with different Endpoint
+
+	app.StaticWeb("/js", "./static/js") // serve our custom javascript code
+
+	app.Get("/", func(ctx *iris.Context) {
+		ctx.Render("client.html", clientPage{"Client Page", ctx.ServerHost()})
+	})
+
+	Conn := make(map[websocket.Connection]bool)
+	var myChatRoom = "room1"
+	var mutex = new(sync.Mutex)
+
+	ws.OnConnection(func(c websocket.Connection) {
+		c.Join(myChatRoom)
+		mutex.Lock()
+		Conn[c] = true
+		mutex.Unlock()
+		c.On("chat", func(message string) {
+			if message == "leave" {
+				c.Leave(myChatRoom)
+				c.To(myChatRoom).Emit("chat", "Client with ID: "+c.ID()+" left from the room and cannot send or receive message to/from this room.")
+				c.Emit("chat", "You have left from the room: "+myChatRoom+" you cannot send or receive any messages from others inside that room.")
+				return
+			}
+		})
+		c.OnDisconnect(func() {
+			mutex.Lock()
+			delete(Conn, c)
+			mutex.Unlock()
+			fmt.Printf("\nConnection with ID: %s has been disconnected!\n", c.ID())
+		})
+	})
+
+	var delay = 1 * time.Second
+	go func() {
+		i := 0
+		for {
+			mutex.Lock()
+			broadcast(Conn, fmt.Sprintf("aaaa %d\n", i))
+			mutex.Unlock()
+			time.Sleep(delay)
+			i++
+		}
+	}()
+
+	go func() {
+		i := 0
+		for {
+			mutex.Lock()
+			broadcast(Conn, fmt.Sprintf("aaaa2 %d\n", i))
+			mutex.Unlock()
+			time.Sleep(delay)
+			i++
+		}
+	}()
+
+	app.Listen(":8080")
+}
+
+func broadcast(Conn map[websocket.Connection]bool, message string) {
+	for k := range Conn {
+		k.To("room1").Emit("chat", message)
+	}
+}
diff --git a/adaptors/websocket/_examples/websocket_connectionlist/static/js/chat.js b/adaptors/websocket/_examples/websocket_connectionlist/static/js/chat.js
new file mode 100644
index 00000000..920a2050
--- /dev/null
+++ b/adaptors/websocket/_examples/websocket_connectionlist/static/js/chat.js
@@ -0,0 +1,38 @@
+var messageTxt;
+var messages;
+
+$(function () {
+
+	messageTxt = $("#messageTxt");
+	messages = $("#messages");
+
+
+	w = new Ws("ws://" + HOST + "/my_endpoint");
+	w.OnConnect(function () {
+		console.log("Websocket connection established");
+	});
+
+	w.OnDisconnect(function () {
+		appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
+	});
+
+	w.On("chat", function (message) {
+		appendMessage($("<div>" + message + "</div>"));
+	});
+
+	$("#sendBtn").click(function () {
+		w.Emit("chat", messageTxt.val().toString());
+		messageTxt.val("");
+	});
+
+})
+
+
+function appendMessage(messageDiv) {
+    var theDiv = messages[0];
+    var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
+    messageDiv.appendTo(messages);
+    if (doScroll) {
+        theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
+    }
+}
diff --git a/adaptors/websocket/_examples/websocket_connectionlist/templates/client.html b/adaptors/websocket/_examples/websocket_connectionlist/templates/client.html
new file mode 100644
index 00000000..04ad2115
--- /dev/null
+++ b/adaptors/websocket/_examples/websocket_connectionlist/templates/client.html
@@ -0,0 +1,24 @@
+<html>
+
+<head>
+<title>{{ .Title}}</title>
+</head>
+
+<body>
+	<div id="messages"
+		style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
+
+	</div>
+	<input type="text" id="messageTxt" />
+	<button type="button" id="sendBtn">Send</button>
+	<script type="text/javascript">
+		var HOST = {{.Host}}
+	</script>
+	<script src="js/vendor/jquery-2.2.3.min.js" type="text/javascript"></script>
+	<!-- This is auto-serving by the Iris, you don't need to have this file in your disk-->
+	<script src="/iris-ws.js" type="text/javascript"></script>
+	<!-- -->
+	<script src="js/chat.js" type="text/javascript"></script>
+</body>
+
+</html>
diff --git a/adaptors/websocket/_examples/websocket_custom_go_client/main.go b/adaptors/websocket/_examples/websocket_custom_go_client/main.go
new file mode 100644
index 00000000..dfb7b43e
--- /dev/null
+++ b/adaptors/websocket/_examples/websocket_custom_go_client/main.go
@@ -0,0 +1,179 @@
+package main
+
+// Run first `go run main.go server`
+// and `go run main.go client` as many times as you want.
+// Originally written by: github.com/antlaw to describe an old kataras/go-websocket's issue
+// which you can find here: https://github.com/kataras/go-websocket/issues/24
+import (
+	"fmt"
+	"os"
+	"strings"
+	"time"
+
+	"gopkg.in/kataras/iris.v6"
+	"gopkg.in/kataras/iris.v6/adaptors/httprouter"
+	"gopkg.in/kataras/iris.v6/adaptors/websocket"
+
+	xwebsocket "golang.org/x/net/websocket"
+)
+
+// WS is the current websocket connection
+var WS *xwebsocket.Conn
+
+func main() {
+	if len(os.Args) == 2 && strings.ToLower(os.Args[1]) == "server" {
+		ServerLoop()
+	} else if len(os.Args) == 2 && strings.ToLower(os.Args[1]) == "client" {
+		ClientLoop()
+	} else {
+		fmt.Println("wsserver [server|client]")
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////
+// client side
+func sendUntilErr(sendInterval int) {
+	i := 1
+	for {
+		time.Sleep(time.Duration(sendInterval) * time.Second)
+		err := SendMessage("2", "all", "objectupdate", "2.UsrSchedule_v1_1")
+		if err != nil {
+			fmt.Println("failed to send join message", err.Error())
+			return
+		}
+		fmt.Println("objectupdate", i)
+		i++
+	}
+}
+
+func recvUntilErr() {
+	var msg = make([]byte, 2048)
+	var n int
+	var err error
+	i := 1
+	for {
+		if n, err = WS.Read(msg); err != nil {
+			fmt.Println(err.Error())
+			return
+		}
+		fmt.Printf("%v Received: %s.%v\n", time.Now(), string(msg[:n]), i)
+		i++
+	}
+
+}
+
+//ConnectWebSocket connect a websocket to host
+func ConnectWebSocket() error {
+	var origin = "http://localhost/"
+	var url = "ws://localhost:9090/socket"
+	var err error
+	WS, err = xwebsocket.Dial(url, "", origin)
+	return err
+}
+
+// CloseWebSocket closes the current websocket connection
+func CloseWebSocket() error {
+	if WS != nil {
+		return WS.Close()
+	}
+	return nil
+}
+
+// SendMessage broadcast a message to server
+func SendMessage(serverID, to, method, message string) error {
+	buffer := []byte(message)
+	return SendtBytes(serverID, to, method, buffer)
+}
+
+// SendtBytes broadcast a message to server
+func SendtBytes(serverID, to, method string, message []byte) error {
+	// look https://github.com/kataras/iris/tree/master/adaptors/websocket/message.go , client.go and client.js
+	// to understand the buffer line:
+	buffer := []byte(fmt.Sprintf("iris-websocket-message:%v;0;%v;%v;", method, serverID, to))
+	buffer = append(buffer, message...)
+	_, err := WS.Write(buffer)
+	if err != nil {
+		fmt.Println(err)
+		return err
+	}
+	return nil
+}
+
+// ClientLoop connects to websocket server, the keep send and recv dataS
+func ClientLoop() {
+	for {
+		time.Sleep(time.Second)
+		err := ConnectWebSocket()
+		if err != nil {
+			fmt.Println("failed to connect websocket", err.Error())
+			continue
+		}
+		// time.Sleep(time.Second)
+		err = SendMessage("2", "all", "join", "dummy2")
+		go sendUntilErr(1)
+		recvUntilErr()
+		err = CloseWebSocket()
+		if err != nil {
+			fmt.Println("failed to close websocket", err.Error())
+		}
+	}
+
+}
+
+/////////////////////////////////////////////////////////////////////////
+// server side
+
+// OnConnect handles incoming websocket connection
+func OnConnect(c websocket.Connection) {
+	fmt.Println("socket.OnConnect()")
+	c.On("join", func(message string) { OnJoin(message, c) })
+	c.On("objectupdate", func(message string) { OnObjectUpdated(message, c) })
+	// ok works too c.EmitMessage([]byte("dsadsa"))
+	c.OnDisconnect(func() { OnDisconnect(c) })
+
+}
+
+// ServerLoop listen and serve websocket requests
+func ServerLoop() {
+	app := iris.New()
+	app.Adapt(iris.DevLogger()) // enable all (error) logs
+	app.Adapt(httprouter.New()) // select the httprouter as the servemux
+
+	ws := websocket.New(websocket.Config{Endpoint: "/socket"})
+	app.Adapt(ws)
+
+	ws.OnConnection(OnConnect)
+	app.Listen("0.0.0.0:9090")
+
+}
+
+// OnJoin handles Join broadcast group request
+func OnJoin(message string, c websocket.Connection) {
+	t := time.Now()
+	c.Join("server2")
+	fmt.Println("OnJoin() time taken:", time.Since(t))
+}
+
+// OnObjectUpdated broadcasts to all client an incoming message
+func OnObjectUpdated(message string, c websocket.Connection) {
+	t := time.Now()
+	s := strings.Split(message, ";")
+	if len(s) != 3 {
+		fmt.Println("OnObjectUpdated() invalid message format:" + message)
+		return
+	}
+	serverID, _, objectID := s[0], s[1], s[2]
+	err := c.To("server"+serverID).Emit("objectupdate", objectID)
+	if err != nil {
+		fmt.Println(err, "failed to broacast object")
+		return
+	}
+	fmt.Println(fmt.Sprintf("OnObjectUpdated() message:%v, time taken: %v", message, time.Since(t)))
+}
+
+// OnDisconnect clean up things when a client is disconnected
+func OnDisconnect(c websocket.Connection) {
+	c.Leave("server2")
+	fmt.Println("OnDisconnect(): client disconnected!")
+
+}
diff --git a/adaptors/websocket/_examples/websocket_native_messages/main.go b/adaptors/websocket/_examples/websocket_native_messages/main.go
new file mode 100644
index 00000000..4634e196
--- /dev/null
+++ b/adaptors/websocket/_examples/websocket_native_messages/main.go
@@ -0,0 +1,62 @@
+package main
+
+import (
+	"fmt"
+
+	"gopkg.in/kataras/iris.v6"
+	"gopkg.in/kataras/iris.v6/adaptors/httprouter"
+	"gopkg.in/kataras/iris.v6/adaptors/view"
+	"gopkg.in/kataras/iris.v6/adaptors/websocket"
+)
+
+/* Native messages no need to import the iris-ws.js to the ./templates.client.html
+Use of: OnMessage and EmitMessage.
+
+
+NOTICE: IF YOU HAVE RAN THE PREVIOUS EXAMPLES YOU HAVE TO CLEAR YOUR BROWSER's CACHE
+BECAUSE chat.js is different than the CACHED. OTHERWISE YOU WILL GET Ws is undefined from the browser's console, becuase it will use the cached.
+*/
+
+type clientPage struct {
+	Title string
+	Host  string
+}
+
+func main() {
+	app := iris.New()
+	app.Adapt(iris.DevLogger())                  // enable all (error) logs
+	app.Adapt(httprouter.New())                  // select the httprouter as the servemux
+	app.Adapt(view.HTML("./templates", ".html")) // select the html engine to serve templates
+
+	ws := websocket.New(websocket.Config{
+		// the path which the websocket client should listen/registed to,
+		Endpoint: "/my_endpoint",
+		// to enable binary messages (useful for protobuf):
+		// BinaryMessages: true,
+	})
+
+	app.Adapt(ws) // adapt the websocket server, you can adapt more than one with different Endpoint
+
+	app.StaticWeb("/js", "./static/js") // serve our custom javascript code
+
+	app.Get("/", func(ctx *iris.Context) {
+		ctx.Render("client.html", clientPage{"Client Page", ctx.Host()})
+	})
+
+	ws.OnConnection(func(c websocket.Connection) {
+
+		c.OnMessage(func(data []byte) {
+			message := string(data)
+			c.To(websocket.Broadcast).EmitMessage([]byte("Message from: " + c.ID() + "-> " + message)) // broadcast to all clients except this
+			c.EmitMessage([]byte("Me: " + message))                                                    // writes to itself
+		})
+
+		c.OnDisconnect(func() {
+			fmt.Printf("\nConnection with ID: %s has been disconnected!", c.ID())
+		})
+
+	})
+
+	app.Listen(":8080")
+
+}
diff --git a/adaptors/websocket/_examples/websocket_native_messages/static/js/chat.js b/adaptors/websocket/_examples/websocket_native_messages/static/js/chat.js
new file mode 100644
index 00000000..344ea3b7
--- /dev/null
+++ b/adaptors/websocket/_examples/websocket_native_messages/static/js/chat.js
@@ -0,0 +1,38 @@
+var messageTxt;
+var messages;
+
+$(function () {
+
+	messageTxt = $("#messageTxt");
+	messages = $("#messages");
+
+
+	w = new WebSocket("ws://" + HOST + "/my_endpoint");
+	w.onopen = function () {
+		console.log("Websocket connection enstablished");
+	};
+
+	w.onclose = function () {
+		appendMessage($("<div><center><h3>Disconnected</h3></center></div>"));
+	};
+	w.onmessage = function(message){
+		appendMessage($("<div>" + message.data + "</div>"));
+	};
+
+
+	$("#sendBtn").click(function () {
+		w.send(messageTxt.val().toString());
+		messageTxt.val("");
+	});
+
+})
+
+
+function appendMessage(messageDiv) {
+    var theDiv = messages[0];
+    var doScroll = theDiv.scrollTop == theDiv.scrollHeight - theDiv.clientHeight;
+    messageDiv.appendTo(messages);
+    if (doScroll) {
+        theDiv.scrollTop = theDiv.scrollHeight - theDiv.clientHeight;
+    }
+}
diff --git a/adaptors/websocket/_examples/websocket_native_messages/templates/client.html b/adaptors/websocket/_examples/websocket_native_messages/templates/client.html
new file mode 100644
index 00000000..312a4143
--- /dev/null
+++ b/adaptors/websocket/_examples/websocket_native_messages/templates/client.html
@@ -0,0 +1,21 @@
+<html>
+
+<head>
+<title>{{ .Title}}</title>
+</head>
+
+<body>
+	<div id="messages"
+		style="border-width: 1px; border-style: solid; height: 400px; width: 375px;">
+
+	</div>
+	<input type="text" id="messageTxt" />
+	<button type="button" id="sendBtn">Send</button>
+	<script type="text/javascript">
+		var HOST = {{.Host}}
+	</script>
+	<script src="js/vendor/jquery-2.2.3.min.js" type="text/javascript"></script>
+	<script src="js/chat.js" type="text/javascript"></script>
+</body>
+
+</html>
diff --git a/adaptors/websocket/client.go b/adaptors/websocket/client.go
new file mode 100644
index 00000000..08bf8f2b
--- /dev/null
+++ b/adaptors/websocket/client.go
@@ -0,0 +1,219 @@
+package websocket
+
+// ------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------
+// ----------------Client side websocket javascript source which is typescript compiled
+// ------------------------------------------------------------------------------------
+// ------------------------------------------------------------------------------------
+
+// ClientSource the client-side javascript raw source code
+var ClientSource = []byte(`var websocketStringMessageType = 0;
+var websocketIntMessageType = 1;
+var websocketBoolMessageType = 2;
+// bytes is missing here for reasons I will explain somewhen
+var websocketJSONMessageType = 4;
+var websocketMessagePrefix = "iris-websocket-message:";
+var websocketMessageSeparator = ";";
+var websocketMessagePrefixLen = websocketMessagePrefix.length;
+var websocketMessageSeparatorLen = websocketMessageSeparator.length;
+var websocketMessagePrefixAndSepIdx = websocketMessagePrefixLen + websocketMessageSeparatorLen - 1;
+var websocketMessagePrefixIdx = websocketMessagePrefixLen - 1;
+var websocketMessageSeparatorIdx = websocketMessageSeparatorLen - 1;
+var Ws = (function () {
+    //
+    function Ws(endpoint, protocols) {
+        var _this = this;
+        // events listeners
+        this.connectListeners = [];
+        this.disconnectListeners = [];
+        this.nativeMessageListeners = [];
+        this.messageListeners = {};
+        if (!window["WebSocket"]) {
+            return;
+        }
+        if (endpoint.indexOf("ws") == -1) {
+            endpoint = "ws://" + endpoint;
+        }
+        if (protocols != null && protocols.length > 0) {
+            this.conn = new WebSocket(endpoint, protocols);
+        }
+        else {
+            this.conn = new WebSocket(endpoint);
+        }
+        this.conn.onopen = (function (evt) {
+            _this.fireConnect();
+            _this.isReady = true;
+            return null;
+        });
+        this.conn.onclose = (function (evt) {
+            _this.fireDisconnect();
+            return null;
+        });
+        this.conn.onmessage = (function (evt) {
+            _this.messageReceivedFromConn(evt);
+        });
+    }
+    //utils
+    Ws.prototype.isNumber = function (obj) {
+        return !isNaN(obj - 0) && obj !== null && obj !== "" && obj !== false;
+    };
+    Ws.prototype.isString = function (obj) {
+        return Object.prototype.toString.call(obj) == "[object String]";
+    };
+    Ws.prototype.isBoolean = function (obj) {
+        return typeof obj === 'boolean' ||
+            (typeof obj === 'object' && typeof obj.valueOf() === 'boolean');
+    };
+    Ws.prototype.isJSON = function (obj) {
+        return typeof obj === 'object';
+    };
+    //
+    // messages
+    Ws.prototype._msg = function (event, websocketMessageType, dataMessage) {
+        return websocketMessagePrefix + event + websocketMessageSeparator + String(websocketMessageType) + websocketMessageSeparator + dataMessage;
+    };
+    Ws.prototype.encodeMessage = function (event, data) {
+        var m = "";
+        var t = 0;
+        if (this.isNumber(data)) {
+            t = websocketIntMessageType;
+            m = data.toString();
+        }
+        else if (this.isBoolean(data)) {
+            t = websocketBoolMessageType;
+            m = data.toString();
+        }
+        else if (this.isString(data)) {
+            t = websocketStringMessageType;
+            m = data.toString();
+        }
+        else if (this.isJSON(data)) {
+            //propably json-object
+            t = websocketJSONMessageType;
+            m = JSON.stringify(data);
+        }
+        else {
+            console.log("Invalid");
+        }
+        return this._msg(event, t, m);
+    };
+    Ws.prototype.decodeMessage = function (event, websocketMessage) {
+        //q-websocket-message;user;4;themarshaledstringfromajsonstruct
+        var skipLen = websocketMessagePrefixLen + websocketMessageSeparatorLen + event.length + 2;
+        if (websocketMessage.length < skipLen + 1) {
+            return null;
+        }
+        var websocketMessageType = parseInt(websocketMessage.charAt(skipLen - 2));
+        var theMessage = websocketMessage.substring(skipLen, websocketMessage.length);
+        if (websocketMessageType == websocketIntMessageType) {
+            return parseInt(theMessage);
+        }
+        else if (websocketMessageType == websocketBoolMessageType) {
+            return Boolean(theMessage);
+        }
+        else if (websocketMessageType == websocketStringMessageType) {
+            return theMessage;
+        }
+        else if (websocketMessageType == websocketJSONMessageType) {
+            return JSON.parse(theMessage);
+        }
+        else {
+            return null; // invalid
+        }
+    };
+    Ws.prototype.getWebsocketCustomEvent = function (websocketMessage) {
+        if (websocketMessage.length < websocketMessagePrefixAndSepIdx) {
+            return "";
+        }
+        var s = websocketMessage.substring(websocketMessagePrefixAndSepIdx, websocketMessage.length);
+        var evt = s.substring(0, s.indexOf(websocketMessageSeparator));
+        return evt;
+    };
+    Ws.prototype.getCustomMessage = function (event, websocketMessage) {
+        var eventIdx = websocketMessage.indexOf(event + websocketMessageSeparator);
+        var s = websocketMessage.substring(eventIdx + event.length + websocketMessageSeparator.length + 2, websocketMessage.length);
+        return s;
+    };
+    //
+    // Ws Events
+    // messageReceivedFromConn this is the func which decides
+    // if it's a native websocket message or a custom qws message
+    // if native message then calls the fireNativeMessage
+    // else calls the fireMessage
+    //
+    // remember q gives you the freedom of native websocket messages if you don't want to use this client side at all.
+    Ws.prototype.messageReceivedFromConn = function (evt) {
+        //check if qws message
+        var message = evt.data;
+        if (message.indexOf(websocketMessagePrefix) != -1) {
+            var event_1 = this.getWebsocketCustomEvent(message);
+            if (event_1 != "") {
+                // it's a custom message
+                this.fireMessage(event_1, this.getCustomMessage(event_1, message));
+                return;
+            }
+        }
+        // it's a native websocket message
+        this.fireNativeMessage(message);
+    };
+    Ws.prototype.OnConnect = function (fn) {
+        if (this.isReady) {
+            fn();
+        }
+        this.connectListeners.push(fn);
+    };
+    Ws.prototype.fireConnect = function () {
+        for (var i = 0; i < this.connectListeners.length; i++) {
+            this.connectListeners[i]();
+        }
+    };
+    Ws.prototype.OnDisconnect = function (fn) {
+        this.disconnectListeners.push(fn);
+    };
+    Ws.prototype.fireDisconnect = function () {
+        for (var i = 0; i < this.disconnectListeners.length; i++) {
+            this.disconnectListeners[i]();
+        }
+    };
+    Ws.prototype.OnMessage = function (cb) {
+        this.nativeMessageListeners.push(cb);
+    };
+    Ws.prototype.fireNativeMessage = function (websocketMessage) {
+        for (var i = 0; i < this.nativeMessageListeners.length; i++) {
+            this.nativeMessageListeners[i](websocketMessage);
+        }
+    };
+    Ws.prototype.On = function (event, cb) {
+        if (this.messageListeners[event] == null || this.messageListeners[event] == undefined) {
+            this.messageListeners[event] = [];
+        }
+        this.messageListeners[event].push(cb);
+    };
+    Ws.prototype.fireMessage = function (event, message) {
+        for (var key in this.messageListeners) {
+            if (this.messageListeners.hasOwnProperty(key)) {
+                if (key == event) {
+                    for (var i = 0; i < this.messageListeners[key].length; i++) {
+                        this.messageListeners[key][i](message);
+                    }
+                }
+            }
+        }
+    };
+    //
+    // Ws Actions
+    Ws.prototype.Disconnect = function () {
+        this.conn.close();
+    };
+    // EmitMessage sends a native websocket message
+    Ws.prototype.EmitMessage = function (websocketMessage) {
+        this.conn.send(websocketMessage);
+    };
+    // Emit sends an q-custom websocket message
+    Ws.prototype.Emit = function (event, data) {
+        var messageStr = this.encodeMessage(event, data);
+        this.EmitMessage(messageStr);
+    };
+    return Ws;
+}());
+`)
diff --git a/adaptors/websocket/client.ts b/adaptors/websocket/client.ts
new file mode 100644
index 00000000..865808af
--- /dev/null
+++ b/adaptors/websocket/client.ts
@@ -0,0 +1,261 @@
+// -------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------
+// ----------------Client side websocket commented typescript source code --------------
+// -------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------
+
+// export to client.go:clientSource []byte
+
+const websocketStringMessageType = 0;
+const websocketIntMessageType = 1;
+const websocketBoolMessageType = 2;
+// bytes is missing here for reasons I will explain somewhen
+const websocketJSONMessageType = 4;
+
+const websocketMessagePrefix = "iris-websocket-message:";
+const websocketMessageSeparator = ";";
+
+const websocketMessagePrefixLen = websocketMessagePrefix.length;
+var websocketMessageSeparatorLen = websocketMessageSeparator.length;
+var websocketMessagePrefixAndSepIdx = websocketMessagePrefixLen + websocketMessageSeparatorLen - 1;
+var websocketMessagePrefixIdx = websocketMessagePrefixLen - 1;
+var websocketMessageSeparatorIdx = websocketMessageSeparatorLen - 1;
+
+type onConnectFunc = () => void;
+type onWebsocketDisconnectFunc = () => void;
+type onWebsocketNativeMessageFunc = (websocketMessage: string) => void;
+type onMessageFunc = (message: any) => void;
+
+class Ws {
+    private conn: WebSocket;
+    private isReady: boolean;
+
+    // events listeners
+
+    private connectListeners: onConnectFunc[] = [];
+    private disconnectListeners: onWebsocketDisconnectFunc[] = [];
+    private nativeMessageListeners: onWebsocketNativeMessageFunc[] = [];
+    private messageListeners: { [event: string]: onMessageFunc[] } = {};
+
+    //
+
+    constructor(endpoint: string, protocols?: string[]) {
+        if (!window["WebSocket"]) {
+            return;
+        }
+
+        if (endpoint.indexOf("ws") == -1) {
+            endpoint = "ws://" + endpoint;
+        }
+        if (protocols != null && protocols.length > 0) {
+            this.conn = new WebSocket(endpoint, protocols);
+        } else {
+            this.conn = new WebSocket(endpoint);
+        }
+
+        this.conn.onopen = ((evt: Event): any => {
+            this.fireConnect();
+            this.isReady = true;
+            return null;
+        });
+
+        this.conn.onclose = ((evt: Event): any => {
+            this.fireDisconnect();
+            return null;
+        });
+
+        this.conn.onmessage = ((evt: MessageEvent) => {
+            this.messageReceivedFromConn(evt);
+        });
+    }
+
+    //utils
+
+    private isNumber(obj: any): boolean {
+        return !isNaN(obj - 0) && obj !== null && obj !== "" && obj !== false;
+    }
+
+    private isString(obj: any): boolean {
+        return Object.prototype.toString.call(obj) == "[object String]";
+    }
+
+    private isBoolean(obj: any): boolean {
+        return typeof obj === 'boolean' ||
+            (typeof obj === 'object' && typeof obj.valueOf() === 'boolean');
+    }
+
+    private isJSON(obj: any): boolean {
+        return typeof obj === 'object';
+    }
+
+    //
+
+    // messages
+    private _msg(event: string, websocketMessageType: number, dataMessage: string): string {
+
+        return websocketMessagePrefix + event + websocketMessageSeparator + String(websocketMessageType) + websocketMessageSeparator + dataMessage;
+    }
+
+    private encodeMessage(event: string, data: any): string {
+        let m = "";
+        let t = 0;
+        if (this.isNumber(data)) {
+            t = websocketIntMessageType;
+            m = data.toString();
+        } else if (this.isBoolean(data)) {
+            t = websocketBoolMessageType;
+            m = data.toString();
+        } else if (this.isString(data)) {
+            t = websocketStringMessageType;
+            m = data.toString();
+        } else if (this.isJSON(data)) {
+            //propably json-object
+            t = websocketJSONMessageType;
+            m = JSON.stringify(data);
+        } else {
+            console.log("Invalid");
+        }
+
+        return this._msg(event, t, m);
+    }
+
+    private decodeMessage<T>(event: string, websocketMessage: string): T | any {
+        //q-websocket-message;user;4;themarshaledstringfromajsonstruct
+        let skipLen = websocketMessagePrefixLen + websocketMessageSeparatorLen + event.length + 2;
+        if (websocketMessage.length < skipLen + 1) {
+            return null;
+        }
+        let websocketMessageType = parseInt(websocketMessage.charAt(skipLen - 2));
+        let theMessage = websocketMessage.substring(skipLen, websocketMessage.length);
+        if (websocketMessageType == websocketIntMessageType) {
+            return parseInt(theMessage);
+        } else if (websocketMessageType == websocketBoolMessageType) {
+            return Boolean(theMessage);
+        } else if (websocketMessageType == websocketStringMessageType) {
+            return theMessage;
+        } else if (websocketMessageType == websocketJSONMessageType) {
+            return JSON.parse(theMessage);
+        } else {
+            return null; // invalid
+        }
+    }
+
+    private getWebsocketCustomEvent(websocketMessage: string): string {
+        if (websocketMessage.length < websocketMessagePrefixAndSepIdx) {
+            return "";
+        }
+        let s = websocketMessage.substring(websocketMessagePrefixAndSepIdx, websocketMessage.length);
+        let evt = s.substring(0, s.indexOf(websocketMessageSeparator));
+
+        return evt;
+    }
+
+    private getCustomMessage(event: string, websocketMessage: string): string {
+        let eventIdx = websocketMessage.indexOf(event + websocketMessageSeparator);
+        let s = websocketMessage.substring(eventIdx + event.length + websocketMessageSeparator.length + 2, websocketMessage.length);
+        return s;
+    }
+
+    //
+
+    // Ws Events
+
+    // messageReceivedFromConn this is the func which decides
+    // if it's a native websocket message or a custom qws message
+    // if native message then calls the fireNativeMessage
+    // else calls the fireMessage
+    //
+    // remember q gives you the freedom of native websocket messages if you don't want to use this client side at all.
+    private messageReceivedFromConn(evt: MessageEvent): void {
+        //check if qws message
+        let message = <string>evt.data;
+        if (message.indexOf(websocketMessagePrefix) != -1) {
+            let event = this.getWebsocketCustomEvent(message);
+            if (event != "") {
+                // it's a custom message
+                this.fireMessage(event, this.getCustomMessage(event, message));
+                return;
+            }
+        }
+
+        // it's a native websocket message
+        this.fireNativeMessage(message);
+    }
+
+    OnConnect(fn: onConnectFunc): void {
+        if (this.isReady) {
+            fn();
+        }
+        this.connectListeners.push(fn);
+    }
+
+    fireConnect(): void {
+        for (let i = 0; i < this.connectListeners.length; i++) {
+            this.connectListeners[i]();
+        }
+    }
+
+    OnDisconnect(fn: onWebsocketDisconnectFunc): void {
+        this.disconnectListeners.push(fn);
+    }
+
+    fireDisconnect(): void {
+        for (let i = 0; i < this.disconnectListeners.length; i++) {
+            this.disconnectListeners[i]();
+        }
+    }
+
+    OnMessage(cb: onWebsocketNativeMessageFunc): void {
+        this.nativeMessageListeners.push(cb);
+    }
+
+    fireNativeMessage(websocketMessage: string): void {
+        for (let i = 0; i < this.nativeMessageListeners.length; i++) {
+            this.nativeMessageListeners[i](websocketMessage);
+        }
+    }
+
+    On(event: string, cb: onMessageFunc): void {
+        if (this.messageListeners[event] == null || this.messageListeners[event] == undefined) {
+            this.messageListeners[event] = [];
+        }
+        this.messageListeners[event].push(cb);
+    }
+
+    fireMessage(event: string, message: any): void {
+        for (let key in this.messageListeners) {
+            if (this.messageListeners.hasOwnProperty(key)) {
+                if (key == event) {
+                    for (let i = 0; i < this.messageListeners[key].length; i++) {
+                        this.messageListeners[key][i](message);
+                    }
+                }
+            }
+        }
+    }
+
+
+    //
+
+    // Ws Actions
+
+    Disconnect(): void {
+        this.conn.close();
+    }
+
+    // EmitMessage sends a native websocket message
+    EmitMessage(websocketMessage: string): void {
+        this.conn.send(websocketMessage);
+    }
+
+    // Emit sends an q-custom websocket message
+    Emit(event: string, data: any): void {
+        let messageStr = this.encodeMessage(event, data);
+        this.EmitMessage(messageStr);
+    }
+
+    //
+
+}
+
+// node-modules export {Ws};
diff --git a/adaptors/websocket/config.go b/adaptors/websocket/config.go
new file mode 100644
index 00000000..2d942ecb
--- /dev/null
+++ b/adaptors/websocket/config.go
@@ -0,0 +1,136 @@
+package websocket
+
+import (
+	"net/http"
+	"time"
+
+	"gopkg.in/kataras/iris.v6"
+)
+
+const (
+	// DefaultWebsocketWriteTimeout 0, no timeout
+	DefaultWebsocketWriteTimeout = 0
+	// DefaultWebsocketReadTimeout 0, no timeout
+	DefaultWebsocketReadTimeout = 0
+	// DefaultWebsocketPongTimeout 60 * time.Second
+	DefaultWebsocketPongTimeout = 60 * time.Second
+	// DefaultWebsocketPingPeriod (DefaultPongTimeout * 9) / 10
+	DefaultWebsocketPingPeriod = (DefaultWebsocketPongTimeout * 9) / 10
+	// DefaultWebsocketMaxMessageSize 1024
+	DefaultWebsocketMaxMessageSize = 1024
+	// DefaultWebsocketReadBufferSize 4096
+	DefaultWebsocketReadBufferSize = 4096
+	// DefaultWebsocketWriterBufferSize 4096
+	DefaultWebsocketWriterBufferSize = 4096
+	// DefaultClientSourcePath "/iris-ws.js"
+	DefaultClientSourcePath = "/iris-ws.js"
+)
+
+var (
+	// DefaultIDGenerator returns the result of 64
+	// random combined characters as the id of a new connection.
+	// Used when config.IDGenerator is nil
+	DefaultIDGenerator = func(*iris.Context) string { return randomString(64) }
+)
+
+// Config the websocket server configuration
+// all of these are optional.
+type Config struct {
+	// Endpoint is the path which the websocket server will listen for clients/connections
+	// Default value is empty string, if you don't set it the Websocket server is disabled.
+	Endpoint string
+	// ClientSourcePath is is the path which the client-side
+	// will be auto-served when the server adapted to an Iris station.
+	// Default value is "/iris-ws.js"
+	ClientSourcePath string
+	Error            func(w http.ResponseWriter, r *http.Request, status int, reason error)
+	CheckOrigin      func(r *http.Request) bool
+	// WriteTimeout time allowed to write a message to the connection.
+	// 0 means no timeout.
+	// Default value is 0
+	WriteTimeout time.Duration
+	// ReadTimeout time allowed to read a message from the connection.
+	// 0 means no timeout.
+	// Default value is 0
+	ReadTimeout time.Duration
+	// PongTimeout allowed to read the next pong message from the connection.
+	// Default value is 60 * time.Second
+	PongTimeout time.Duration
+	// PingPeriod send ping messages to the connection with this period. Must be less than PongTimeout.
+	// Default value is 60 *time.Second
+	PingPeriod time.Duration
+	// MaxMessageSize max message size allowed from connection.
+	// Default value is 1024
+	MaxMessageSize int64
+	// BinaryMessages set it to true in order to denotes binary data messages instead of utf-8 text
+	// compatible if you wanna use the Connection's EmitMessage to send a custom binary data to the client, like a native server-client communication.
+	// defaults to false
+	BinaryMessages bool
+	// ReadBufferSize is the buffer size for the underline reader
+	// Default value is 4096
+	ReadBufferSize int
+	// WriteBufferSize is the buffer size for the underline writer
+	// Default value is 4096
+	WriteBufferSize int
+	// IDGenerator used to create (and later on, set)
+	// an ID for each incoming websocket connections (clients).
+	// The request is an argument which you can use to generate the ID (from headers for example).
+	// If empty then the ID is generated by DefaultIDGenerator: randomString(64)
+	IDGenerator func(ctx *iris.Context) string
+}
+
+// Validate validates the configuration
+func (c Config) Validate() Config {
+
+	if c.ClientSourcePath == "" {
+		c.ClientSourcePath = DefaultClientSourcePath
+	}
+
+	// 0 means no timeout.
+	if c.WriteTimeout < 0 {
+		c.WriteTimeout = DefaultWebsocketWriteTimeout
+	}
+
+	if c.ReadTimeout < 0 {
+		c.ReadTimeout = DefaultWebsocketReadTimeout
+	}
+
+	if c.PongTimeout < 0 {
+		c.PongTimeout = DefaultWebsocketPongTimeout
+	}
+
+	if c.PingPeriod <= 0 {
+		c.PingPeriod = DefaultWebsocketPingPeriod
+	}
+
+	if c.MaxMessageSize <= 0 {
+		c.MaxMessageSize = DefaultWebsocketMaxMessageSize
+	}
+
+	if c.ReadBufferSize <= 0 {
+		c.ReadBufferSize = DefaultWebsocketReadBufferSize
+	}
+
+	if c.WriteBufferSize <= 0 {
+		c.WriteBufferSize = DefaultWebsocketWriterBufferSize
+	}
+
+	if c.Error == nil {
+		c.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) {
+			//empty
+		}
+	}
+
+	if c.CheckOrigin == nil {
+		c.CheckOrigin = func(r *http.Request) bool {
+			// allow all connections by default
+			return true
+		}
+	}
+
+	if c.IDGenerator == nil {
+		c.IDGenerator = DefaultIDGenerator
+	}
+
+	return c
+}
diff --git a/adaptors/websocket/connection.go b/adaptors/websocket/connection.go
new file mode 100644
index 00000000..1434211f
--- /dev/null
+++ b/adaptors/websocket/connection.go
@@ -0,0 +1,382 @@
+package websocket
+
+import (
+	"bytes"
+	"io"
+	"net"
+	"strconv"
+	"sync"
+	"time"
+
+	"github.com/gorilla/websocket"
+	"gopkg.in/kataras/iris.v6"
+)
+
+// UnderlineConnection is used for compatible with fasthttp and net/http underline websocket libraries
+// we only need ~8 funcs from websocket.Conn so:
+type UnderlineConnection interface {
+	// SetWriteDeadline sets the write deadline on the underlying network
+	// connection. After a write has timed out, the websocket state is corrupt and
+	// all future writes will return an error. A zero value for t means writes will
+	// not time out.
+	SetWriteDeadline(t time.Time) error
+	// SetReadDeadline sets the read deadline on the underlying network connection.
+	// After a read has timed out, the websocket connection state is corrupt and
+	// all future reads will return an error. A zero value for t means reads will
+	// not time out.
+	SetReadDeadline(t time.Time) error
+	// SetReadLimit sets the maximum size for a message read from the peer. If a
+	// message exceeds the limit, the connection sends a close frame to the peer
+	// and returns ErrReadLimit to the application.
+	SetReadLimit(limit int64)
+	// SetPongHandler sets the handler for pong messages received from the peer.
+	// The appData argument to h is the PONG frame application data. The default
+	// pong handler does nothing.
+	SetPongHandler(h func(appData string) error)
+	// SetPingHandler sets the handler for ping messages received from the peer.
+	// The appData argument to h is the PING frame application data. The default
+	// ping handler sends a pong to the peer.
+	SetPingHandler(h func(appData string) error)
+	// WriteControl writes a control message with the given deadline. The allowed
+	// message types are CloseMessage, PingMessage and PongMessage.
+	WriteControl(messageType int, data []byte, deadline time.Time) error
+	// WriteMessage is a helper method for getting a writer using NextWriter,
+	// writing the message and closing the writer.
+	WriteMessage(messageType int, data []byte) error
+	// ReadMessage is a helper method for getting a reader using NextReader and
+	// reading from that reader to a buffer.
+	ReadMessage() (messageType int, p []byte, err error)
+	// NextWriter returns a writer for the next message to send. The writer's Close
+	// method flushes the complete message to the network.
+	//
+	// There can be at most one open writer on a connection. NextWriter closes the
+	// previous writer if the application has not already done so.
+	NextWriter(messageType int) (io.WriteCloser, error)
+	// Close closes the underlying network connection without sending or waiting for a close frame.
+	Close() error
+}
+
+// -------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------
+// -------------------------------Connection implementation-----------------------------
+// -------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------
+
+type (
+	// DisconnectFunc is the callback which fires when a client/connection closed
+	DisconnectFunc func()
+	// ErrorFunc is the callback which fires when an error happens
+	ErrorFunc (func(string))
+	// NativeMessageFunc is the callback for native websocket messages, receives one []byte parameter which is the raw client's message
+	NativeMessageFunc func([]byte)
+	// MessageFunc is the second argument to the Emitter's Emit functions.
+	// A callback which should receives one parameter of type string, int, bool or any valid JSON/Go struct
+	MessageFunc interface{}
+	// Connection is the front-end API that you will use to communicate with the client side
+	Connection interface {
+		// Emitter implements EmitMessage & Emit
+		Emitter
+		// ID returns the connection's identifier
+		ID() string
+
+		// Context returns the (upgraded) *iris.Context of this connection
+		// avoid using it, you normally don't need it,
+		// websocket has everything you need to authenticate the user BUT if it's necessary
+		// then  you use it to receive user information, for example: from headers
+		Context() *iris.Context
+
+		// OnDisconnect registers a callback which fires when this connection is closed by an error or manual
+		OnDisconnect(DisconnectFunc)
+		// OnError registers a callback which fires when this connection occurs an error
+		OnError(ErrorFunc)
+		// EmitError can be used to send a custom error message to the connection
+		//
+		// It does nothing more than firing the OnError listeners. It doesn't sends anything to the client.
+		EmitError(errorMessage string)
+		// To defines where server should send a message
+		// returns an emitter to send messages
+		To(string) Emitter
+		// OnMessage registers a callback which fires when native websocket message received
+		OnMessage(NativeMessageFunc)
+		// On registers a callback to a particular event which fires when a message to this event received
+		On(string, MessageFunc)
+		// Join join a connection to a room, it doesn't check if connection is already there, so care
+		Join(string)
+		// Leave removes a connection from a room
+		Leave(string)
+		// Disconnect disconnects the client, close the underline websocket conn and removes it from the conn list
+		// returns the error, if any, from the underline connection
+		Disconnect() error
+	}
+
+	connection struct {
+		underline                UnderlineConnection
+		id                       string
+		messageType              int
+		pinger                   *time.Ticker
+		disconnected             bool
+		onDisconnectListeners    []DisconnectFunc
+		onErrorListeners         []ErrorFunc
+		onNativeMessageListeners []NativeMessageFunc
+		onEventListeners         map[string][]MessageFunc
+		// these were  maden for performance only
+		self      Emitter // pre-defined emitter than sends message to its self client
+		broadcast Emitter // pre-defined emitter that sends message to all except this
+		all       Emitter // pre-defined emitter which sends message to all clients
+
+		// access to the Context, use with causion, you can't use response writer as you imagine.
+		ctx    *iris.Context
+		server *server
+		// #119 , websocket writers are not protected by locks inside the gorilla's websocket code
+		// so we must protect them otherwise we're getting concurrent connection error on multi writers in the same time.
+		writerMu sync.Mutex
+		// same exists for reader look here: https://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages
+		// but we only use one reader in one goroutine, so we are safe.
+		// readerMu sync.Mutex
+	}
+)
+
+var _ Connection = &connection{}
+
+func newConnection(s *server, ctx *iris.Context, underlineConn UnderlineConnection, id string) *connection {
+	c := &connection{
+		underline:                underlineConn,
+		id:                       id,
+		messageType:              websocket.TextMessage,
+		onDisconnectListeners:    make([]DisconnectFunc, 0),
+		onErrorListeners:         make([]ErrorFunc, 0),
+		onNativeMessageListeners: make([]NativeMessageFunc, 0),
+		onEventListeners:         make(map[string][]MessageFunc, 0),
+		ctx:                      ctx,
+		server:                   s,
+	}
+
+	if s.config.BinaryMessages {
+		c.messageType = websocket.BinaryMessage
+	}
+
+	c.self = newEmitter(c, c.id)
+	c.broadcast = newEmitter(c, Broadcast)
+	c.all = newEmitter(c, All)
+
+	return c
+}
+
+// write writes a raw websocket message with a specific type to the client
+// used by ping messages and any CloseMessage types.
+func (c *connection) write(websocketMessageType int, data []byte) {
+	// for any-case the app tries to write from different goroutines,
+	// we must protect them because they're reporting that as bug...
+	c.writerMu.Lock()
+	if writeTimeout := c.server.config.WriteTimeout; writeTimeout > 0 {
+		// set the write deadline based on the configuration
+		c.underline.SetWriteDeadline(time.Now().Add(writeTimeout))
+	}
+
+	// .WriteMessage same as NextWriter and close (flush)
+	err := c.underline.WriteMessage(websocketMessageType, data)
+	c.writerMu.Unlock()
+	if err != nil {
+		// if failed then the connection is off, fire the disconnect
+		c.Disconnect()
+	}
+}
+
+// writeDefault is the same as write but the message type is the configured by c.messageType
+// if BinaryMessages is enabled then it's raw []byte as you expected to work with protobufs
+func (c *connection) writeDefault(data []byte) {
+	c.write(c.messageType, data)
+}
+
+const (
+	// WriteWait is 1 second at the internal implementation,
+	// same as here but this can be changed at the future*
+	WriteWait = 1 * time.Second
+)
+
+func (c *connection) startPinger() {
+
+	// this is the default internal handler, we just change the writeWait because of the actions we must do before
+	// the server sends the ping-pong.
+
+	pingHandler := func(message string) error {
+		err := c.underline.WriteControl(websocket.PongMessage, []byte(message), time.Now().Add(WriteWait))
+		if err == websocket.ErrCloseSent {
+			return nil
+		} else if e, ok := err.(net.Error); ok && e.Temporary() {
+			return nil
+		}
+		return err
+	}
+
+	c.underline.SetPingHandler(pingHandler)
+
+	// start a new timer ticker based on the configuration
+	c.pinger = time.NewTicker(c.server.config.PingPeriod)
+
+	go func() {
+		for {
+			// wait for each tick
+			<-c.pinger.C
+			// try to ping the client, if failed then it disconnects
+			c.write(websocket.PingMessage, []byte{})
+		}
+	}()
+}
+
+func (c *connection) startReader() {
+	conn := c.underline
+	hasReadTimeout := c.server.config.ReadTimeout > 0
+
+	conn.SetReadLimit(c.server.config.MaxMessageSize)
+	conn.SetPongHandler(func(s string) error {
+		if hasReadTimeout {
+			conn.SetReadDeadline(time.Now().Add(c.server.config.ReadTimeout))
+		}
+
+		return nil
+	})
+
+	defer func() {
+		c.Disconnect()
+	}()
+
+	for {
+		if hasReadTimeout {
+			// set the read deadline based on the configuration
+			conn.SetReadDeadline(time.Now().Add(c.server.config.ReadTimeout))
+		}
+
+		_, data, err := conn.ReadMessage()
+		if err != nil {
+			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
+				c.EmitError(err.Error())
+			}
+			break
+		} else {
+			c.messageReceived(data)
+		}
+
+	}
+
+}
+
+// messageReceived checks the incoming message and fire the nativeMessage listeners or the event listeners (ws custom message)
+func (c *connection) messageReceived(data []byte) {
+
+	if bytes.HasPrefix(data, websocketMessagePrefixBytes) {
+		customData := string(data)
+		//it's a custom ws message
+		receivedEvt := getWebsocketCustomEvent(customData)
+		listeners := c.onEventListeners[receivedEvt]
+		if listeners == nil { // if not listeners for this event exit from here
+			return
+		}
+		customMessage, err := websocketMessageDeserialize(receivedEvt, customData)
+		if customMessage == nil || err != nil {
+			return
+		}
+
+		for i := range listeners {
+			if fn, ok := listeners[i].(func()); ok { // its a simple func(){} callback
+				fn()
+			} else if fnString, ok := listeners[i].(func(string)); ok {
+
+				if msgString, is := customMessage.(string); is {
+					fnString(msgString)
+				} else if msgInt, is := customMessage.(int); is {
+					// here if server side waiting for string but client side sent an int, just convert this int to a string
+					fnString(strconv.Itoa(msgInt))
+				}
+
+			} else if fnInt, ok := listeners[i].(func(int)); ok {
+				fnInt(customMessage.(int))
+			} else if fnBool, ok := listeners[i].(func(bool)); ok {
+				fnBool(customMessage.(bool))
+			} else if fnBytes, ok := listeners[i].(func([]byte)); ok {
+				fnBytes(customMessage.([]byte))
+			} else {
+				listeners[i].(func(interface{}))(customMessage)
+			}
+
+		}
+	} else {
+		// it's native websocket message
+		for i := range c.onNativeMessageListeners {
+			c.onNativeMessageListeners[i](data)
+		}
+	}
+
+}
+
+func (c *connection) ID() string {
+	return c.id
+}
+
+func (c *connection) Context() *iris.Context {
+	return c.ctx
+}
+
+func (c *connection) fireDisconnect() {
+	for i := range c.onDisconnectListeners {
+		c.onDisconnectListeners[i]()
+	}
+}
+
+func (c *connection) OnDisconnect(cb DisconnectFunc) {
+	c.onDisconnectListeners = append(c.onDisconnectListeners, cb)
+}
+
+func (c *connection) OnError(cb ErrorFunc) {
+	c.onErrorListeners = append(c.onErrorListeners, cb)
+}
+
+func (c *connection) EmitError(errorMessage string) {
+	for _, cb := range c.onErrorListeners {
+		cb(errorMessage)
+	}
+}
+
+func (c *connection) To(to string) Emitter {
+	if to == Broadcast { // if send to all except me, then return the pre-defined emitter, and so on
+		return c.broadcast
+	} else if to == All {
+		return c.all
+	} else if to == c.id {
+		return c.self
+	}
+	// is an emitter to another client/connection
+	return newEmitter(c, to)
+}
+
+func (c *connection) EmitMessage(nativeMessage []byte) error {
+	return c.self.EmitMessage(nativeMessage)
+}
+
+func (c *connection) Emit(event string, message interface{}) error {
+	return c.self.Emit(event, message)
+}
+
+func (c *connection) OnMessage(cb NativeMessageFunc) {
+	c.onNativeMessageListeners = append(c.onNativeMessageListeners, cb)
+}
+
+func (c *connection) On(event string, cb MessageFunc) {
+	if c.onEventListeners[event] == nil {
+		c.onEventListeners[event] = make([]MessageFunc, 0)
+	}
+
+	c.onEventListeners[event] = append(c.onEventListeners[event], cb)
+}
+
+func (c *connection) Join(roomName string) {
+	c.server.Join(roomName, c.id)
+}
+
+func (c *connection) Leave(roomName string) {
+	c.server.Leave(roomName, c.id)
+}
+
+func (c *connection) Disconnect() error {
+	return c.server.Disconnect(c.ID())
+}
diff --git a/adaptors/websocket/emitter.go b/adaptors/websocket/emitter.go
new file mode 100644
index 00000000..6a68de6f
--- /dev/null
+++ b/adaptors/websocket/emitter.go
@@ -0,0 +1,49 @@
+package websocket
+
+// -------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------
+// --------------------------------Emitter implementation-------------------------------
+// -------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------
+
+const (
+	// All is the string which the Emitter use to send a message to all
+	All = ""
+	// Broadcast is the string which the Emitter use to send a message to all except this connection
+	Broadcast = ";gowebsocket;to;all;except;me;"
+)
+
+type (
+	// Emitter is the message/or/event manager
+	Emitter interface {
+		// EmitMessage sends a native websocket message
+		EmitMessage([]byte) error
+		// Emit sends a message on a particular event
+		Emit(string, interface{}) error
+	}
+
+	emitter struct {
+		conn *connection
+		to   string
+	}
+)
+
+var _ Emitter = &emitter{}
+
+func newEmitter(c *connection, to string) *emitter {
+	return &emitter{conn: c, to: to}
+}
+
+func (e *emitter) EmitMessage(nativeMessage []byte) error {
+	e.conn.server.emitMessage(e.conn.id, e.to, nativeMessage)
+	return nil
+}
+
+func (e *emitter) Emit(event string, data interface{}) error {
+	message, err := websocketMessageSerialize(event, data)
+	if err != nil {
+		return err
+	}
+	e.EmitMessage([]byte(message))
+	return nil
+}
diff --git a/adaptors/websocket/message.go b/adaptors/websocket/message.go
new file mode 100644
index 00000000..f7ac748e
--- /dev/null
+++ b/adaptors/websocket/message.go
@@ -0,0 +1,188 @@
+package websocket
+
+import (
+	"encoding/json"
+	"github.com/kataras/go-errors"
+	"github.com/valyala/bytebufferpool"
+	"math/rand"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// -------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------
+// -----------------websocket messages and de/serialization implementation--------------
+// -------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------
+
+/*
+serializer, [de]websocketMessageSerialize the messages from the client to the websocketServer and from the websocketServer to the client
+*/
+
+// The same values are exists on client side also
+const (
+	websocketStringMessageType websocketMessageType = iota
+	websocketIntMessageType
+	websocketBoolMessageType
+	websocketBytesMessageType
+	websocketJSONMessageType
+)
+
+const (
+	websocketMessagePrefix          = "iris-websocket-message:"
+	websocketMessageSeparator       = ";"
+	websocketMessagePrefixLen       = len(websocketMessagePrefix)
+	websocketMessageSeparatorLen    = len(websocketMessageSeparator)
+	websocketMessagePrefixAndSepIdx = websocketMessagePrefixLen + websocketMessageSeparatorLen - 1
+	websocketMessagePrefixIdx       = websocketMessagePrefixLen - 1
+	websocketMessageSeparatorIdx    = websocketMessageSeparatorLen - 1
+)
+
+var (
+	websocketMessageSeparatorByte = websocketMessageSeparator[0]
+	websocketMessageBuffer        = bytebufferpool.Pool{}
+	websocketMessagePrefixBytes   = []byte(websocketMessagePrefix)
+)
+
+type (
+	websocketMessageType uint8
+)
+
+func (m websocketMessageType) String() string {
+	return strconv.Itoa(int(m))
+}
+
+func (m websocketMessageType) Name() string {
+	if m == websocketStringMessageType {
+		return "string"
+	} else if m == websocketIntMessageType {
+		return "int"
+	} else if m == websocketBoolMessageType {
+		return "bool"
+	} else if m == websocketBytesMessageType {
+		return "[]byte"
+	} else if m == websocketJSONMessageType {
+		return "json"
+	}
+
+	return "Invalid(" + m.String() + ")"
+
+}
+
+// websocketMessageSerialize serializes a custom websocket message from websocketServer to be delivered to the client
+// returns the  string form of the message
+// Supported data types are: string, int, bool, bytes and JSON.
+func websocketMessageSerialize(event string, data interface{}) (string, error) {
+	var msgType websocketMessageType
+	var dataMessage string
+
+	if s, ok := data.(string); ok {
+		msgType = websocketStringMessageType
+		dataMessage = s
+	} else if i, ok := data.(int); ok {
+		msgType = websocketIntMessageType
+		dataMessage = strconv.Itoa(i)
+	} else if b, ok := data.(bool); ok {
+		msgType = websocketBoolMessageType
+		dataMessage = strconv.FormatBool(b)
+	} else if by, ok := data.([]byte); ok {
+		msgType = websocketBytesMessageType
+		dataMessage = string(by)
+	} else {
+		//we suppose is json
+		res, err := json.Marshal(data)
+		if err != nil {
+			return "", err
+		}
+		msgType = websocketJSONMessageType
+		dataMessage = string(res)
+	}
+
+	b := websocketMessageBuffer.Get()
+	b.WriteString(websocketMessagePrefix)
+	b.WriteString(event)
+	b.WriteString(websocketMessageSeparator)
+	b.WriteString(msgType.String())
+	b.WriteString(websocketMessageSeparator)
+	b.WriteString(dataMessage)
+	dataMessage = b.String()
+	websocketMessageBuffer.Put(b)
+
+	return dataMessage, nil
+
+}
+
+var errInvalidTypeMessage = errors.New("Type %s is invalid for message: %s")
+
+// websocketMessageDeserialize deserializes a custom websocket message from the client
+// ex: iris-websocket-message;chat;4;themarshaledstringfromajsonstruct will return 'hello' as string
+// Supported data types are: string, int, bool, bytes and JSON.
+func websocketMessageDeserialize(event string, websocketMessage string) (message interface{}, err error) {
+	t, formaterr := strconv.Atoi(websocketMessage[websocketMessagePrefixAndSepIdx+len(event)+1 : websocketMessagePrefixAndSepIdx+len(event)+2]) // in order to iris-websocket-message;user;-> 4
+	if formaterr != nil {
+		return nil, formaterr
+	}
+	_type := websocketMessageType(t)
+	_message := websocketMessage[websocketMessagePrefixAndSepIdx+len(event)+3:] // in order to iris-websocket-message;user;4; -> themarshaledstringfromajsonstruct
+
+	if _type == websocketStringMessageType {
+		message = string(_message)
+	} else if _type == websocketIntMessageType {
+		message, err = strconv.Atoi(_message)
+	} else if _type == websocketBoolMessageType {
+		message, err = strconv.ParseBool(_message)
+	} else if _type == websocketBytesMessageType {
+		message = []byte(_message)
+	} else if _type == websocketJSONMessageType {
+		err = json.Unmarshal([]byte(_message), &message)
+	} else {
+		return nil, errInvalidTypeMessage.Format(_type.Name(), websocketMessage)
+	}
+
+	return
+}
+
+// getWebsocketCustomEvent return empty string when the websocketMessage is native message
+func getWebsocketCustomEvent(websocketMessage string) string {
+	if len(websocketMessage) < websocketMessagePrefixAndSepIdx {
+		return ""
+	}
+	s := websocketMessage[websocketMessagePrefixAndSepIdx:]
+	evt := s[:strings.IndexByte(s, websocketMessageSeparatorByte)]
+	return evt
+}
+
+const (
+	letterBytes   = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+	letterIdxBits = 6                    // 6 bits to represent a letter index
+	letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
+	letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
+)
+
+var src = rand.NewSource(time.Now().UnixNano())
+
+// random takes a parameter (int) and returns random slice of byte
+// ex: var randomstrbytes []byte; randomstrbytes = utils.Random(32)
+func random(n int) []byte {
+	b := make([]byte, n)
+	// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
+	for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
+		if remain == 0 {
+			cache, remain = src.Int63(), letterIdxMax
+		}
+		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
+			b[i] = letterBytes[idx]
+			i--
+		}
+		cache >>= letterIdxBits
+		remain--
+	}
+
+	return b
+}
+
+// randomString accepts a number(10 for example) and returns a random string using simple but fairly safe random algorithm
+func randomString(n int) string {
+	return string(random(n))
+}
diff --git a/adaptors/websocket/server.go b/adaptors/websocket/server.go
new file mode 100644
index 00000000..300c1a11
--- /dev/null
+++ b/adaptors/websocket/server.go
@@ -0,0 +1,388 @@
+package websocket
+
+import (
+	"sync"
+
+	"github.com/gorilla/websocket"
+	"gopkg.in/kataras/iris.v6"
+)
+
+// Server is the websocket server,
+// listens on the config's port, the critical part is the event OnConnection
+type Server interface {
+	// Adapt implements the iris' adaptor, it adapts the websocket server to an Iris station.
+	// see websocket.go
+	Adapt(frame *iris.Policies)
+
+	// Handler returns the iris.HandlerFunc
+	// which is setted to the 'Websocket Endpoint path',
+	// the client should target to this handler's developer's custom path
+	// ex: iris.Default.Any("/myendpoint", mywebsocket.Handler())
+	Handler() iris.HandlerFunc
+
+	// OnConnection this is the main event you, as developer, will work with each of the websocket connections
+	OnConnection(cb ConnectionFunc)
+
+	/*
+	   connection actions, same as the connection's method,
+	    but these methods accept the connection ID,
+	    which is useful when the developer maps
+	    this id with a database field (using config.IDGenerator).
+	*/
+
+	// IsConnected returns true if the connection with that ID is connected to the server
+	// useful when you have defined a custom connection id generator (based on a database)
+	// and you want to check if that connection is already connected (on multiple tabs)
+	IsConnected(connID string) bool
+
+	// Join joins a websocket client to a room,
+	// first parameter is the room name and the second the connection.ID()
+	//
+	// You can use connection.Join("room name") instead.
+	Join(roomName string, connID string)
+
+	// LeaveAll kicks out a connection from ALL of its joined rooms
+	LeaveAll(connID string)
+
+	// Leave leaves a websocket client from a room,
+	// first parameter is the room name and the second the connection.ID()
+	//
+	// You can use connection.Leave("room name") instead.
+	Leave(roomName string, connID string)
+
+	// Disconnect force-disconnects a websocket connection
+	// based on its connection.ID()
+	// What it does?
+	// 1. remove the connection from the list
+	// 2. leave from all joined rooms
+	// 3. fire the disconnect callbacks, if any
+	// 4. close the underline connection and return its error, if any.
+	//
+	// You can use the connection.Disconnect() instead.
+	Disconnect(connID string) error
+}
+
+// -------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------
+// --------------------------------Connection key-based list----------------------------
+// -------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------
+
+type connectionKV struct {
+	key   string // the connection ID
+	value *connection
+}
+
+type connections []connectionKV
+
+func (cs *connections) add(key string, value *connection) {
+	args := *cs
+	n := len(args)
+	// check if already id/key exist, if yes replace the conn
+	for i := 0; i < n; i++ {
+		kv := &args[i]
+		if kv.key == key {
+			kv.value = value
+			return
+		}
+	}
+
+	c := cap(args)
+	// make the connections slice bigger and put the conn
+	if c > n {
+		args = args[:n+1]
+		kv := &args[n]
+		kv.key = key
+		kv.value = value
+		*cs = args
+		return
+	}
+	// append to the connections slice and put the conn
+	kv := connectionKV{}
+	kv.key = key
+	kv.value = value
+	*cs = append(args, kv)
+}
+
+func (cs *connections) get(key string) *connection {
+	args := *cs
+	n := len(args)
+	for i := 0; i < n; i++ {
+		kv := &args[i]
+		if kv.key == key {
+			return kv.value
+		}
+	}
+	return nil
+}
+
+// returns the connection which removed and a bool value of found or not
+// the connection is useful to fire the disconnect events, we use that form in order to
+// make work things faster without the need of get-remove, just -remove should do the job.
+func (cs *connections) remove(key string) (*connection, bool) {
+	args := *cs
+	n := len(args)
+	for i := 0; i < n; i++ {
+		kv := &args[i]
+		if kv.key == key {
+			conn := kv.value
+			// we found the index,
+			// let's remove the item by appending to the temp and
+			// after set the pointer of the slice to this temp args
+			args = append(args[:i], args[i+1:]...)
+			*cs = args
+			return conn, true
+		}
+	}
+	return nil, false
+}
+
+// -------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------
+// --------------------------------Server implementation--------------------------------
+// -------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------
+
+type (
+	// ConnectionFunc is the callback which fires when a client/connection is connected to the server.
+	// Receives one parameter which is the Connection
+	ConnectionFunc func(Connection)
+
+	// websocketRoomPayload is used as payload from the connection to the server
+	websocketRoomPayload struct {
+		roomName     string
+		connectionID string
+	}
+
+	// payloads, connection -> server
+	websocketMessagePayload struct {
+		from string
+		to   string
+		data []byte
+	}
+
+	server struct {
+		config                Config
+		connections           connections
+		rooms                 map[string][]string // by default a connection is joined to a room which has the connection id as its name
+		mu                    sync.Mutex          // for rooms
+		onConnectionListeners []ConnectionFunc
+		//connectionPool        *sync.Pool // sadly I can't make this because the websocket connection is live until is closed.
+	}
+)
+
+var _ Server = &server{}
+
+// server implementation
+
+func (s *server) Handler() iris.HandlerFunc {
+	// build the upgrader once
+	c := s.config
+
+	upgrader := websocket.Upgrader{ReadBufferSize: c.ReadBufferSize, WriteBufferSize: c.WriteBufferSize, Error: c.Error, CheckOrigin: c.CheckOrigin}
+	return func(ctx *iris.Context) {
+		// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
+		//
+		// The responseHeader is included in the response to the client's upgrade
+		// request. Use the responseHeader to specify cookies (Set-Cookie) and the
+		// application negotiated subprotocol (Sec--Protocol).
+		//
+		// If the upgrade fails, then Upgrade replies to the client with an HTTP error
+		// response.
+		conn, err := upgrader.Upgrade(ctx.ResponseWriter, ctx.Request, ctx.ResponseWriter.Header())
+		if err != nil {
+			ctx.Log(iris.DevMode, "websocket error: "+err.Error())
+			ctx.EmitError(iris.StatusServiceUnavailable)
+			return
+		}
+		s.handleConnection(ctx, conn)
+	}
+}
+
+// handleConnection creates & starts to listening to a new connection
+func (s *server) handleConnection(ctx *iris.Context, websocketConn UnderlineConnection) {
+	// use the config's id generator (or the default) to create a websocket client/connection id
+	cid := s.config.IDGenerator(ctx)
+	// create the new connection
+	c := newConnection(s, ctx, websocketConn, cid)
+	// add the connection to the server's list
+	s.connections.add(cid, c)
+
+	// join to itself
+	s.Join(c.ID(), c.ID())
+
+	// NOTE TO ME: fire these first BEFORE startReader and startPinger
+	// in order to set the events and any messages to send
+	// the startPinger will send the OK to the client and only
+	// then the client is able to send and receive from server
+	// when all things are ready and only then. DO NOT change this order.
+
+	// fire the on connection event callbacks, if any
+	for i := range s.onConnectionListeners {
+		s.onConnectionListeners[i](c)
+	}
+
+	// start the ping
+	c.startPinger()
+
+	// start the messages reader
+	c.startReader()
+}
+
+/* Notes:
+   We use the id as the signature of the connection because with the custom IDGenerator
+	 the developer can share this ID with a database field, so we want to give the oportunnity to handle
+	 his/her websocket connections without even use the connection itself.
+
+	 Another question may be:
+	 Q: Why you use server as the main actioner for all of the connection actions?
+	 	  For example the server.Disconnect(connID) manages the connection internal fields, is this code-style correct?
+	 A: It's the correct code-style for these type of applications and libraries, server manages all, the connnection's functions
+	 should just do some internal checks (if needed) and push the action to its parent, which is the server, the server is able to
+	 remove a connection, the rooms of its connected and all these things, so in order to not split the logic, we have the main logic
+	 here, in the server, and let the connection with some exported functions whose exists for the per-connection action user's code-style.
+
+	 Ok my english are s** I can feel it, but these comments are mostly for me.
+*/
+
+// OnConnection this is the main event you, as developer, will work with each of the websocket connections
+func (s *server) OnConnection(cb ConnectionFunc) {
+	s.onConnectionListeners = append(s.onConnectionListeners, cb)
+}
+
+// IsConnected returns true if the connection with that ID is connected to the server
+// useful when you have defined a custom connection id generator (based on a database)
+// and you want to check if that connection is already connected (on multiple tabs)
+func (s *server) IsConnected(connID string) bool {
+	c := s.connections.get(connID)
+	return c != nil
+}
+
+// Join joins a websocket client to a room,
+// first parameter is the room name and the second the connection.ID()
+//
+// You can use connection.Join("room name") instead.
+func (s *server) Join(roomName string, connID string) {
+	s.mu.Lock()
+	s.join(roomName, connID)
+	s.mu.Unlock()
+}
+
+// join used internally, no locks used.
+func (s *server) join(roomName string, connID string) {
+	if s.rooms[roomName] == nil {
+		s.rooms[roomName] = make([]string, 0)
+	}
+	s.rooms[roomName] = append(s.rooms[roomName], connID)
+}
+
+// LeaveAll kicks out a connection from ALL of its joined rooms
+func (s *server) LeaveAll(connID string) {
+	s.mu.Lock()
+	for name, connectionIDs := range s.rooms {
+		for i := range connectionIDs {
+			if connectionIDs[i] == connID {
+				// the connection is inside this room, lets remove it
+				s.rooms[name][i] = s.rooms[name][len(s.rooms[name])-1]
+				s.rooms[name] = s.rooms[name][:len(s.rooms[name])-1]
+			}
+		}
+	}
+	s.mu.Unlock()
+}
+
+// Leave leaves a websocket client from a room,
+// first parameter is the room name and the second the connection.ID()
+//
+// You can use connection.Leave("room name") instead.
+func (s *server) Leave(roomName string, connID string) {
+	s.mu.Lock()
+	s.leave(roomName, connID)
+	s.mu.Unlock()
+}
+
+// leave used internally, no locks used.
+func (s *server) leave(roomName string, connID string) {
+	///THINK: we could add locks to its room but we still use the lock for the whole rooms or we can just do what we do with connections
+	// I will think about it on the next revision, so far we use the locks only for rooms so we are ok...
+	if s.rooms[roomName] != nil {
+		for i := range s.rooms[roomName] {
+			if s.rooms[roomName][i] == connID {
+				s.rooms[roomName][i] = s.rooms[roomName][len(s.rooms[roomName])-1]
+				s.rooms[roomName] = s.rooms[roomName][:len(s.rooms[roomName])-1]
+				break
+			}
+		}
+		if len(s.rooms[roomName]) == 0 { // if room is empty then delete it
+			delete(s.rooms, roomName)
+		}
+	}
+}
+
+// emitMessage is the main 'router' of the messages coming from the connection
+// this is the main function which writes the RAW websocket messages to the client.
+// It sends them(messages) to the correct room (self, broadcast or to specific client)
+//
+// You don't have to use this generic method, exists only for extreme
+// apps which you have an external goroutine with a list of custom connection list.
+//
+// You SHOULD use connection.EmitMessage/Emit/To().Emit/EmitMessage instead.
+// let's keep it unexported for the best.
+func (s *server) emitMessage(from, to string, data []byte) {
+	if to != All && to != Broadcast && s.rooms[to] != nil {
+		// it suppose to send the message to a specific room/or a user inside its own room
+		for _, connectionIDInsideRoom := range s.rooms[to] {
+			if c := s.connections.get(connectionIDInsideRoom); c != nil {
+				c.writeDefault(data) //send the message to the client(s)
+			} else {
+				// the connection is not connected but it's inside the room, we remove it on disconnect but for ANY CASE:
+				cid := connectionIDInsideRoom
+				if c != nil {
+					cid = c.id
+				}
+				s.Leave(cid, to)
+			}
+		}
+	} else {
+		// it suppose to send the message to all opened connections or to all except the sender
+		for _, cKV := range s.connections {
+			connID := cKV.key
+			if to != All && to != connID { // if it's not suppose to send to all connections (including itself)
+				if to == Broadcast && from == connID { // if broadcast to other connections except this
+					continue //here we do the opossite of previous block,
+					// just skip this connection when it's suppose to send the message to all connections except the sender
+				}
+
+			}
+			// send to the client(s) when the top validators passed
+			cKV.value.writeDefault(data)
+		}
+	}
+}
+
+// Disconnect force-disconnects a websocket connection based on its connection.ID()
+// What it does?
+// 1. remove the connection from the list
+// 2. leave from all joined rooms
+// 3. fire the disconnect callbacks, if any
+// 4. close the underline connection and return its error, if any.
+//
+// You can use the connection.Disconnect() instead.
+func (s *server) Disconnect(connID string) (err error) {
+	// remove the connection from the list
+	if c, ok := s.connections.remove(connID); ok {
+		if !c.disconnected {
+			c.disconnected = true
+			// stop the ping timer
+			c.pinger.Stop()
+			// leave from all joined rooms
+			s.LeaveAll(connID)
+			// fire the disconnect callbacks, if any
+			c.fireDisconnect()
+			// close the underline connection and return its error, if any.
+			err = c.underline.Close()
+		}
+	}
+
+	return
+}
diff --git a/adaptors/websocket/websocket.go b/adaptors/websocket/websocket.go
new file mode 100644
index 00000000..9e92ef54
--- /dev/null
+++ b/adaptors/websocket/websocket.go
@@ -0,0 +1,62 @@
+// Package websocket provides an easy way to setup server and client side rich websocket experience for Iris
+package websocket
+
+import (
+	"strings"
+
+	"gopkg.in/kataras/iris.v6"
+)
+
+// New returns a new websocket server policy adaptor.
+func New(cfg Config) Server {
+	return &server{
+		config: cfg.Validate(),
+		rooms:  make(map[string][]string, 0),
+		onConnectionListeners: make([]ConnectionFunc, 0),
+	}
+}
+
+func fixPath(s string) string {
+	if s == "" {
+		return ""
+	}
+
+	if s[0] != '/' {
+		s = "/" + s
+	}
+
+	s = strings.Replace(s, "//", "/", -1)
+	return s
+}
+
+// Adapt implements the iris' adaptor, it adapts the websocket server to an Iris station.
+func (s *server) Adapt(frame *iris.Policies) {
+	// bind the server's Handler to Iris at Boot state
+	evt := iris.EventPolicy{
+		Boot: func(f *iris.Framework) {
+			wsPath := fixPath(s.config.Endpoint)
+			if wsPath == "" {
+				f.Log(iris.DevMode, "websocket's configuration field 'Endpoint' cannot be empty, websocket server stops")
+				return
+			}
+
+			wsClientSidePath := fixPath(s.config.ClientSourcePath)
+			if wsClientSidePath == "" {
+				f.Log(iris.DevMode, "websocket's configuration field 'ClientSourcePath' cannot be empty, websocket server stops")
+				return
+			}
+
+			// set the routing for client-side source (javascript) (optional)
+			clientSideLookupName := "iris-websocket-client-side"
+			wsHandler := s.Handler()
+			f.Get(wsPath, wsHandler)
+			// check if client side doesn't already exists
+			if f.Routes().Lookup(clientSideLookupName) == nil {
+				// serve the client side on domain:port/iris-ws.js
+				f.StaticContent(wsClientSidePath, "application/javascript", ClientSource).ChangeName(clientSideLookupName)
+			}
+		},
+	}
+
+	evt.Adapt(frame)
+}
diff --git a/configuration.go b/configuration.go
index 1858104d..a7005d97 100644
--- a/configuration.go
+++ b/configuration.go
@@ -4,7 +4,6 @@ import (
 	"crypto/tls"
 	"net"
 	"net/http"
-	"net/url"
 	"strconv"
 	"time"
 
@@ -17,11 +16,12 @@ type (
 	// OptionSetter sets a configuration field to the main configuration
 	// used to help developers to write less and configure only what they really want and nothing else
 	// example:
-	// iris.New(iris.Configuration{Sessions:iris.SessionConfiguration{Cookie:"mysessionid"}, Websocket: iris.WebsocketConfiguration{Endpoint:"/my_endpoint"}})
+	// iris.New(iris.Configuration{Charset: "UTF-8", Gzip:true})
 	// now can be done also by using iris.Option$FIELD:
-	// iris.New(irisOptionSessionsCookie("mycookieid"),iris.OptionWebsocketEndpoint("my_endpoint"))
+	// iris.New(iris.OptionCharset("UTF-8"), iris.OptionGzip(true))
 	// benefits:
-	// 1. user/dev have no worries what option to pass, he/she can just press iris.Option and all options should be shown to her/his editor's autocomplete-popup window
+	// 1. dev has no worries what option to pass,
+	//    he/she can just press iris.Option and all options should be shown to her/his editor's autocomplete-popup window
 	// 2. can be passed with any order
 	// 3. Can override previous configuration
 	OptionSetter interface {
@@ -177,9 +177,6 @@ type Configuration struct {
 	// Sessions contains the configs for sessions
 	Sessions SessionsConfiguration
 
-	// Websocket contains the configs for Websocket's server integration
-	Websocket WebsocketConfiguration
-
 	// Other are the custom, dynamic options, can be empty
 	// this fill used only by you to set any app's options you want
 	// for each of an Iris instance
@@ -443,7 +440,6 @@ func DefaultConfiguration() Configuration {
 		Charset:                           DefaultCharset,
 		Gzip:                              false,
 		Sessions:                          DefaultSessionsConfiguration(),
-		Websocket:                         DefaultWebsocketConfiguration(),
 		Other:                             options.Options{},
 	}
 }
@@ -529,184 +525,6 @@ func DefaultSessionsConfiguration() SessionsConfiguration {
 	}
 }
 
-// WebsocketConfiguration the config contains options for the Websocket main config field
-type WebsocketConfiguration struct {
-	// WriteTimeout time allowed to write a message to the connection.
-	// Default value is 15 * time.Second
-	WriteTimeout time.Duration
-	// PongTimeout allowed to read the next pong message from the connection
-	// Default value is 60 * time.Second
-	PongTimeout time.Duration
-	// PingPeriod send ping messages to the connection with this period. Must be less than PongTimeout
-	// Default value is (PongTimeout * 9) / 10
-	PingPeriod time.Duration
-	// MaxMessageSize max message size allowed from connection
-	// Default value is 1024
-	MaxMessageSize int64
-	// BinaryMessages set it to true in order to denotes binary data messages instead of utf-8 text
-	// see https://github.com/kataras/iris/issues/387#issuecomment-243006022 for more
-	// Defaults to false
-	BinaryMessages bool
-	// Endpoint is the path which the websocket server will listen for clients/connections
-	// Default value is empty string, if you don't set it the Websocket server is disabled.
-	Endpoint string
-	// ReadBufferSize is the buffer size for the underline reader
-	ReadBufferSize int
-	// WriteBufferSize is the buffer size for the underline writer
-	WriteBufferSize int
-	// Error specifies the function for generating HTTP error responses.
-	//
-	// The default behavior is to store the reason in the context (ctx.Set(reason)) and fire any custom error (ctx.EmitError(status))
-	Error func(ctx *Context, status int, reason error)
-	// CheckOrigin returns true if the request Origin header is acceptable. If
-	// CheckOrigin is nil, the host in the Origin header must not be set or
-	// must match the host of the request.
-	//
-	// The default behavior is to allow all origins
-	// you can change this behavior by setting the iris.Default.Config.Websocket.CheckOrigin = iris.WebsocketCheckSameOrigin
-	CheckOrigin func(r *http.Request) bool
-	// IDGenerator used to create (and later on, set)
-	// an ID for each incoming websocket connections (clients).
-	// If empty then the ID is generated by the result of 64
-	// random combined characters
-	IDGenerator func(r *http.Request) string
-}
-
-var (
-	// OptionWebsocketWriteTimeout time allowed to write a message to the connection.
-	// Default value is 15 * time.Second
-	OptionWebsocketWriteTimeout = func(val time.Duration) OptionSet {
-		return func(c *Configuration) {
-			c.Websocket.WriteTimeout = val
-		}
-	}
-	// OptionWebsocketPongTimeout allowed to read the next pong message from the connection
-	// Default value is 60 * time.Second
-	OptionWebsocketPongTimeout = func(val time.Duration) OptionSet {
-		return func(c *Configuration) {
-			c.Websocket.PongTimeout = val
-		}
-	}
-	// OptionWebsocketPingPeriod send ping messages to the connection with this period. Must be less than PongTimeout
-	// Default value is (PongTimeout * 9) / 10
-	OptionWebsocketPingPeriod = func(val time.Duration) OptionSet {
-		return func(c *Configuration) {
-			c.Websocket.PingPeriod = val
-		}
-	}
-	// OptionWebsocketMaxMessageSize max message size allowed from connection
-	// Default value is 1024
-	OptionWebsocketMaxMessageSize = func(val int64) OptionSet {
-		return func(c *Configuration) {
-			c.Websocket.MaxMessageSize = val
-		}
-	}
-	// OptionWebsocketBinaryMessages set it to true in order to denotes binary data messages instead of utf-8 text
-	// see https://github.com/kataras/iris/issues/387#issuecomment-243006022 for more
-	// Defaults to false
-	OptionWebsocketBinaryMessages = func(val bool) OptionSet {
-		return func(c *Configuration) {
-			c.Websocket.BinaryMessages = val
-		}
-	}
-	// OptionWebsocketEndpoint is the path which the websocket server will listen for clients/connections
-	// Default value is empty string, if you don't set it the Websocket server is disabled.
-	OptionWebsocketEndpoint = func(val string) OptionSet {
-		return func(c *Configuration) {
-			c.Websocket.Endpoint = val
-		}
-	}
-	// OptionWebsocketReadBufferSize is the buffer size for the underline reader
-	OptionWebsocketReadBufferSize = func(val int) OptionSet {
-		return func(c *Configuration) {
-			c.Websocket.ReadBufferSize = val
-		}
-	}
-	// OptionWebsocketWriteBufferSize is the buffer size for the underline writer
-	OptionWebsocketWriteBufferSize = func(val int) OptionSet {
-		return func(c *Configuration) {
-			c.Websocket.WriteBufferSize = val
-		}
-	}
-
-	// OptionWebsocketError specifies the function for generating HTTP error responses.
-	OptionWebsocketError = func(val func(*Context, int, error)) OptionSet {
-		return func(c *Configuration) {
-			c.Websocket.Error = val
-		}
-	}
-	// OptionWebsocketCheckOrigin returns true if the request Origin header is acceptable. If
-	// CheckOrigin is nil, the host in the Origin header must not be set or
-	// must match the host of the request.
-	OptionWebsocketCheckOrigin = func(val func(*http.Request) bool) OptionSet {
-		return func(c *Configuration) {
-			c.Websocket.CheckOrigin = val
-		}
-	}
-
-	// OptionWebsocketIDGenerator used to create (and later on, set)
-	// an ID for each incoming websocket connections (clients).
-	// If empty then the ID is generated by the result of 64
-	// random combined characters
-	OptionWebsocketIDGenerator = func(val func(*http.Request) string) OptionSet {
-		return func(c *Configuration) {
-			c.Websocket.IDGenerator = val
-		}
-	}
-)
-
-const (
-	// DefaultWebsocketWriteTimeout 15 * time.Second
-	DefaultWebsocketWriteTimeout = 15 * time.Second
-	// DefaultWebsocketPongTimeout 60 * time.Second
-	DefaultWebsocketPongTimeout = 60 * time.Second
-	// DefaultWebsocketPingPeriod (DefaultPongTimeout * 9) / 10
-	DefaultWebsocketPingPeriod = (DefaultWebsocketPongTimeout * 9) / 10
-	// DefaultWebsocketMaxMessageSize 1024
-	DefaultWebsocketMaxMessageSize = 1024
-)
-
-var (
-	// DefaultWebsocketError is the default method to manage the handshake websocket errors
-	DefaultWebsocketError = func(ctx *Context, status int, reason error) {
-		ctx.Set("WsError", reason)
-		ctx.EmitError(status)
-	}
-	// DefaultWebsocketCheckOrigin is the default method to allow websocket clients to connect to this server
-	// you can change this behavior by setting the iris.Default.Config.Websocket.CheckOrigin = iris.WebsocketCheckSameOrigin
-	DefaultWebsocketCheckOrigin = func(r *http.Request) bool {
-		return true
-	}
-	// WebsocketCheckSameOrigin returns true if the origin is not set or is equal to the request host
-	WebsocketCheckSameOrigin = func(r *http.Request) bool {
-		origin := r.Header.Get("origin")
-		if len(origin) == 0 {
-			return true
-		}
-		u, err := url.Parse(origin)
-		if err != nil {
-			return false
-		}
-		return u.Host == r.Host
-	}
-)
-
-// DefaultWebsocketConfiguration returns the default config for iris-ws websocket package
-func DefaultWebsocketConfiguration() WebsocketConfiguration {
-	return WebsocketConfiguration{
-		WriteTimeout:    DefaultWebsocketWriteTimeout,
-		PongTimeout:     DefaultWebsocketPongTimeout,
-		PingPeriod:      DefaultWebsocketPingPeriod,
-		MaxMessageSize:  DefaultWebsocketMaxMessageSize,
-		BinaryMessages:  false,
-		ReadBufferSize:  4096,
-		WriteBufferSize: 4096,
-		Endpoint:        "",
-		// use the kataras/go-websocket default
-		IDGenerator: nil,
-	}
-}
-
 // Default values for base Server conf
 const (
 	// DefaultServerHostname returns the default hostname which is 0.0.0.0
diff --git a/doc.go b/doc.go
index 08edcb91..9159e898 100644
--- a/doc.go
+++ b/doc.go
@@ -513,11 +513,11 @@ Example code:
 You should have a basic idea of the framework by now, we just scratched the surface.
 If you enjoy what you just saw and want to learn more, please follow the below links:
 
-    - examples: https://github.com/iris-contrib/examples
-    - book: https://docs.iris-go.com
-    - adaptors: https://github.com/kataras/iris/tree/v6/adaptors
-    - middleware: https://github.com/kataras/iris/tree/v6/middleware & https://github.com/iris-contrib/middleware
-    - godocs: https://godoc.org/github.com/kataras/iris
+- examples: https://github.com/iris-contrib/examples
+- book: https://docs.iris-go.com
+- adaptors: https://github.com/kataras/iris/tree/v6/adaptors
+- middleware: https://github.com/kataras/iris/tree/v6/middleware & https://github.com/iris-contrib/middleware
+- godocs: https://godoc.org/github.com/kataras/iris
 
 
 */
diff --git a/iris.go b/iris.go
index c0817bef..0055d552 100644
--- a/iris.go
+++ b/iris.go
@@ -70,10 +70,9 @@ type Framework struct {
 	ln             net.Listener
 	closedManually bool
 
-	once      sync.Once
-	Config    *Configuration
-	sessions  sessions.Sessions
-	Websocket *WebsocketServer
+	once     sync.Once
+	Config   *Configuration
+	sessions sessions.Sessions
 }
 
 var defaultGlobalLoggerOuput = log.New(os.Stdout, "[iris] ", log.LstdFlags)
@@ -253,19 +252,6 @@ func New(setters ...OptionSetter) *Framework {
 		}})
 	}
 
-	{
-		//  +------------------------------------------------------------+
-		//  | Module Name: Websocket                                     |
-		//  | On Init: Attach a new websocket server.                    |
-		//  |         It starts on first callback registration           |
-		//  +------------------------------------------------------------+
-
-		// in order to be able to call $instance.Websocket.OnConnection.
-		// The whole server's configuration will be
-		// initialized on the first OnConnection registration (no runtime)
-		s.Websocket = NewWebsocketServer(s)
-	}
-
 	{
 		//  +------------------------------------------------------------+
 		//  | Module Name: Router                                        |
diff --git a/middleware/basicauth/LICENSE b/middleware/basicauth/LICENSE
new file mode 100644
index 00000000..2935ad5d
--- /dev/null
+++ b/middleware/basicauth/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016-2017 Gerasimos Maropoulos
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/middleware/i18n/LICENSE b/middleware/i18n/LICENSE
index d0978209..eb4883c1 100644
--- a/middleware/i18n/LICENSE
+++ b/middleware/i18n/LICENSE
@@ -1,3 +1,25 @@
+The MIT License (MIT)
+
+Copyright (c) 2016-2017 Gerasimos Maropoulos
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
 Apache License
 Version 2.0, January 2004
 http://www.apache.org/licenses/
diff --git a/middleware/logger/LICENSE b/middleware/logger/LICENSE
new file mode 100644
index 00000000..2935ad5d
--- /dev/null
+++ b/middleware/logger/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016-2017 Gerasimos Maropoulos
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/middleware/recover/LICENSE b/middleware/recover/LICENSE
new file mode 100644
index 00000000..2935ad5d
--- /dev/null
+++ b/middleware/recover/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016-2017 Gerasimos Maropoulos
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/websocket.go b/websocket.go
deleted file mode 100644
index cb30ca0b..00000000
--- a/websocket.go
+++ /dev/null
@@ -1,104 +0,0 @@
-package iris
-
-import (
-	"net/http"
-	"sync"
-
-	"github.com/kataras/go-websocket"
-)
-
-// conversionals
-const (
-	// All is the string which the Emitter use to send a message to all
-	All = websocket.All
-	// NotMe is the string which the Emitter use to send a message to all except this websocket.Connection
-	NotMe = websocket.NotMe
-	// Broadcast is the string which the Emitter use to send a message to all except this websocket.Connection, same as 'NotMe'
-	Broadcast = websocket.Broadcast
-)
-
-// Note I keep this code only to no change the front-end API, we could only use the go-websocket and set our custom upgrader
-
-type (
-	// WebsocketServer is the iris websocket server, expose the websocket.Server
-	// the below code is a wrapper and bridge between iris-contrib/websocket and kataras/go-websocket
-	WebsocketServer struct {
-		websocket.Server
-		station *Framework
-		once    sync.Once
-		// Config:
-		// if endpoint is not empty then this configuration is used instead of the station's
-		// useful when the user/dev wants more than one websocket server inside one iris instance.
-		Config WebsocketConfiguration
-	}
-)
-
-// NewWebsocketServer returns a new empty unitialized websocket server
-// it runs on first OnConnection
-func NewWebsocketServer(station *Framework) *WebsocketServer {
-	return &WebsocketServer{station: station, Server: websocket.New(), Config: station.Config.Websocket}
-}
-
-// NewWebsocketServer creates the client side source route and the route path Endpoint with the correct Handler
-// receives the websocket configuration and  the iris station
-// and returns the websocket server which can be attached to more than one iris station (if needed)
-func (ws *WebsocketServer) init() {
-
-	if ws.Config.Endpoint == "" {
-		ws.Config = ws.station.Config.Websocket
-	}
-
-	c := ws.Config
-
-	if c.Endpoint == "" {
-		return
-	}
-
-	if c.CheckOrigin == nil {
-		c.CheckOrigin = DefaultWebsocketCheckOrigin
-	}
-
-	if c.Error == nil {
-		c.Error = DefaultWebsocketError
-	}
-	// set the underline websocket server's configuration
-	ws.Server.Set(websocket.Config{
-		WriteTimeout:    c.WriteTimeout,
-		PongTimeout:     c.PongTimeout,
-		PingPeriod:      c.PingPeriod,
-		MaxMessageSize:  c.MaxMessageSize,
-		BinaryMessages:  c.BinaryMessages,
-		ReadBufferSize:  c.ReadBufferSize,
-		WriteBufferSize: c.WriteBufferSize,
-		Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) {
-			ws.station.Context.Run(w, r, func(ctx *Context) {
-				c.Error(ctx, status, reason)
-			})
-		},
-		CheckOrigin: c.CheckOrigin,
-		IDGenerator: c.IDGenerator,
-	})
-
-	// set the routing for client-side source (javascript) (optional)
-	clientSideLookupName := "iris-websocket-client-side"
-	ws.station.Get(c.Endpoint, ToHandler(ws.Server.Handler()))
-	// check if client side already exists
-	if ws.station.Routes().Lookup(clientSideLookupName) == nil {
-		// serve the client side on domain:port/iris-ws.js
-		ws.station.StaticContent("/iris-ws.js", contentJavascript, websocket.ClientSource).ChangeName(clientSideLookupName)
-	}
-}
-
-// WebsocketConnection is the front-end API that you will use to communicate with the client side
-type WebsocketConnection interface {
-	websocket.Connection
-}
-
-// OnConnection this is the main event you, as developer, will work with each of the websocket connections
-func (ws *WebsocketServer) OnConnection(connectionListener func(WebsocketConnection)) {
-	ws.once.Do(ws.init)
-
-	ws.Server.OnConnection(func(c websocket.Connection) {
-		connectionListener(c)
-	})
-}