diff --git a/net/http/inc/THttpWSHandler.h b/net/http/inc/THttpWSHandler.h
index 92e081a4714d2f879e89bd6b8d3e379635a2d82d..8685d4200d755c61fc2f2872f537c14e474034c2 100644
--- a/net/http/inc/THttpWSHandler.h
+++ b/net/http/inc/THttpWSHandler.h
@@ -14,11 +14,27 @@
 
 #include "TNamed.h"
 
+#include "TList.h"
+
 class THttpCallArg;
+class THttpWSEngine;
+class THttpServer;
 
 class THttpWSHandler : public TNamed {
 
+friend class THttpServer;
+
+private:
+
+   THttpWSEngine *FindEngine(UInt_t id) const;
+
+   Bool_t DirecltyHandle(THttpCallArg *arg);
+
+
 protected:
+
+   TList    fEngines;         ///<!  list of of engines in use, cleaned automatically at the end
+
    THttpWSHandler(const char *name, const char *title);
 
 public:
@@ -31,6 +47,15 @@ public:
    /// Used by the webcanvas
    virtual TString GetDefaultPageContent() { return ""; }
 
+   /// Return kTRUE if websocket with given ID exists
+   Bool_t HasWS(UInt_t wsid) const { return FindEngine(wsid) != 0; }
+
+   void CloseWS(UInt_t wsid);
+
+   void SendWS(UInt_t wsid, const void *buf, int len);
+
+   void SendCharStarWS(UInt_t wsid, const char *str);
+
    virtual Bool_t ProcessWS(THttpCallArg *arg) = 0;
 
    ClassDef(THttpWSHandler, 0) // abstract class for handling websocket requests
diff --git a/net/http/src/THttpServer.cxx b/net/http/src/THttpServer.cxx
index dd296f1aa8a306d7cc2e63a85514da7093e7067b..c11cf3743204dc18c031b4fa4cb2bcd048fce7c8 100644
--- a/net/http/src/THttpServer.cxx
+++ b/net/http/src/THttpServer.cxx
@@ -863,7 +863,7 @@ void THttpServer::ProcessRequest(THttpCallArg *arg)
       THttpWSHandler *handler = dynamic_cast<THttpWSHandler *>(fSniffer->FindTObjectInHierarchy(arg->fPathName.Data()));
 
       if (handler) {
-         handler->ProcessWS(arg);
+         handler->DirecltyHandle(arg);
       } else {
          arg->Set404();
       }
@@ -881,7 +881,7 @@ void THttpServer::ProcessRequest(THttpCallArg *arg)
          // if accepted, reply with connection id, which must be used in the following communications
          arg->SetMethod("WS_CONNECT");
 
-         if (handler && handler->ProcessWS(arg)) {
+         if (handler && handler->DirecltyHandle(arg)) {
             arg->SetMethod("WS_READY");
 
             TLongPollEngine *handle = new TLongPollEngine("longpoll", arg->fPathName.Data());
@@ -889,7 +889,7 @@ void THttpServer::ProcessRequest(THttpCallArg *arg)
             arg->SetWSId(handle->GetId());
             arg->SetWSHandle(handle);
 
-            if (handler->ProcessWS(arg)) {
+            if (handler->DirecltyHandle(arg)) {
                arg->SetContent(TString::Format("%u", arg->GetWSId()));
                arg->SetContentType("text/plain");
             }
@@ -921,7 +921,7 @@ void THttpServer::ProcessRequest(THttpCallArg *arg)
                arg->SetPostData(buf, len / 2);
             }
          }
-         if (handler && !handler->ProcessWS(arg))
+         if (handler && !handler->DirecltyHandle(arg))
             arg->Set404();
       }
       return;
@@ -930,11 +930,11 @@ void THttpServer::ProcessRequest(THttpCallArg *arg)
       // ROOT emulation of websocket
       THttpWSHandler *handler = dynamic_cast<THttpWSHandler *>(fSniffer->FindTObjectInHierarchy(arg->fPathName.Data()));
 
-      if (!handler || !handler->ProcessWS(arg))
+      if (!handler || !handler->DirecltyHandle(arg))
          arg->Set404();
       if (!arg->Is404() && (arg->fMethod == "WS_CONNECT") && arg->fWSHandle) {
          arg->SetMethod("WS_READY");
-         if (handler->ProcessWS(arg)) {
+         if (handler->DirecltyHandle(arg)) {
             arg->SetContent(TString::Format("%u", arg->GetWSId()));
             arg->SetContentType("text/plain");
          } else {
diff --git a/net/http/src/THttpWSHandler.cxx b/net/http/src/THttpWSHandler.cxx
index 5bf476d0cbf8cc718416c11f9935594ab3d262e3..49baddc9397c2d44cd4f38ee0cd6afd9bbbc038d 100644
--- a/net/http/src/THttpWSHandler.cxx
+++ b/net/http/src/THttpWSHandler.cxx
@@ -11,6 +11,10 @@
 
 #include "THttpWSHandler.h"
 
+#include "THttpWSEngine.h"
+#include "THttpCallArg.h"
+
+
 //////////////////////////////////////////////////////////////////////////
 //                                                                      //
 // THttpWSHandler                                                       //
@@ -25,10 +29,104 @@ ClassImp(THttpWSHandler);
 /// normal constructor
 
 THttpWSHandler::THttpWSHandler(const char *name, const char *title) :
-   TNamed(name, title)
+   TNamed(name, title), fEngines()
 {
 }
 
 THttpWSHandler::~THttpWSHandler()
 {
+   TIter iter(&fEngines);
+   THttpWSEngine *engine = 0;
+
+   while ((engine = (THttpWSEngine *)iter()) != 0)
+      engine->ClearHandle();
+
+   fEngines.Delete();
+}
+
+
+THttpWSEngine *THttpWSHandler::FindEngine(UInt_t id) const
+{
+   TIter iter(&fEngines);
+   THttpWSEngine *engine = 0;
+
+   while ((engine = (THttpWSEngine *)iter()) != 0) {
+      if (engine->GetId() == id) return engine;
+   }
+
+   return 0;
+}
+
+Bool_t THttpWSHandler::DirecltyHandle(THttpCallArg *arg)
+{
+   if (!arg->GetWSId()) return ProcessWS(arg);
+
+   THttpWSEngine* engine = FindEngine(arg->GetWSId());
+
+   if (strcmp(arg->GetMethod(), "WS_CONNECT") == 0) {
+      // accept all requests, in future one could limit number of connections
+      return ProcessWS(arg);
+   }
+
+   if (strcmp(arg->GetMethod(), "WS_READY") == 0) {
+
+      if (engine) {
+         Error("DirecltyHandle","WS engine with similar id exists %u\n", arg->GetWSId());
+         fEngines.Remove(engine);
+         delete engine;
+      }
+
+      THttpWSEngine *wshandle = dynamic_cast<THttpWSEngine *>(arg->TakeWSHandle());
+
+      fEngines.Add(wshandle);
+
+      if (!ProcessWS(arg)) {
+         // if connection refused, remove engine again
+         fEngines.Remove(wshandle);
+         delete wshandle;
+         return kFALSE;
+      }
+
+      return kTRUE;
+   }
+
+   if (strcmp(arg->GetMethod(), "WS_CLOSE") == 0) {
+      // connection is closed, one can remove handle
+
+      if (engine) {
+         engine->ClearHandle();
+         fEngines.Remove(engine);
+         delete engine;
+      }
+
+      return ProcessWS(arg);
+   }
+
+   if (engine && engine->PreviewData(arg)) return kTRUE;
+
+   return ProcessWS(arg);
+}
+
+void THttpWSHandler::CloseWS(UInt_t wsid)
+{
+   THttpWSEngine* engine = FindEngine(wsid);
+
+   if (engine) {
+      fEngines.Remove(engine);
+      delete engine;
+   }
+}
+
+void THttpWSHandler::SendWS(UInt_t wsid, const void *buf, int len)
+{
+   THttpWSEngine* engine = FindEngine(wsid);
+
+   if (engine) engine->Send(buf, len);
+}
+
+void THttpWSHandler::SendCharStarWS(UInt_t wsid, const char *str)
+{
+   THttpWSEngine* engine = FindEngine(wsid);
+
+   if (engine) engine->SendCharStar(str);
 }