diff --git a/libsrc/webserver/CMakeLists.txt b/libsrc/webserver/CMakeLists.txt
index 12e5345f..70c67052 100644
--- a/libsrc/webserver/CMakeLists.txt
+++ b/libsrc/webserver/CMakeLists.txt
@@ -1,3 +1,4 @@
+find_package(Qt${QT_VERSION_MAJOR} COMPONENTS WebSockets REQUIRED)
 
 file(GLOB_RECURSE webFiles RELATIVE ${CMAKE_BINARY_DIR}  ${CMAKE_SOURCE_DIR}/assets/webconfig/*)
 file(RELATIVE_PATH webConfigPath ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/assets/webconfig)
@@ -28,13 +29,13 @@ add_library(webserver
 	${CMAKE_SOURCE_DIR}/libsrc/webserver/StaticFileServing.cpp
 	${CMAKE_SOURCE_DIR}/libsrc/webserver/WebJsonRpc.h
 	${CMAKE_SOURCE_DIR}/libsrc/webserver/WebJsonRpc.cpp
-	${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketClient.h
-	${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketClient.cpp
-	${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketUtils.h
+	${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketJsonHandler.h
+	${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketJsonHandler.cpp
 	${CMAKE_BINARY_DIR}/WebConfig.qrc
-)
+ )
 
 target_link_libraries(webserver
+	Qt${QT_VERSION_MAJOR}::WebSockets
 	hyperion
 	hyperion-utils
 	hyperion-api
diff --git a/libsrc/webserver/QtHttpClientWrapper.cpp b/libsrc/webserver/QtHttpClientWrapper.cpp
index ea9aeb0d..9ace9448 100644
--- a/libsrc/webserver/QtHttpClientWrapper.cpp
+++ b/libsrc/webserver/QtHttpClientWrapper.cpp
@@ -5,7 +5,7 @@
 #include "QtHttpReply.h"
 #include "QtHttpServer.h"
 #include "QtHttpHeader.h"
-#include "WebSocketClient.h"
+#include "WebSocketJsonHandler.h"
 #include "WebJsonRpc.h"
 
 #include <QCryptographicHash>
@@ -29,6 +29,8 @@ QtHttpClientWrapper::QtHttpClientWrapper (QTcpSocket * sock, const bool& localCo
 	, m_webJsonRpc     (nullptr)
 {
 	connect (m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived);
+	connect(&m_websocketServer, &QWebSocketServer::newConnection,
+		this, &QtHttpClientWrapper::onNewWebSocketConnection);
 }
 
 QString QtHttpClientWrapper::getGuid (void)
@@ -50,6 +52,11 @@ void QtHttpClientWrapper::onClientDataReceived (void)
 {
 	if (m_sockClient != Q_NULLPTR)
 	{
+		if (!m_sockClient->isTransactionStarted())
+		{
+			m_sockClient->startTransaction();
+		}
+
 		while (m_sockClient->bytesAvailable () != 0)
 		{
 			QByteArray line = m_sockClient->readLine ();
@@ -162,22 +169,25 @@ void QtHttpClientWrapper::onClientDataReceived (void)
 			{
 			case RequestParsed: // a valid request has ben fully parsed
 			{
-				// Catch websocket header "Upgrade"
-				if(m_currentRequest->getHeader(QtHttpHeader::Upgrade).toLower() == "websocket")
-				{
+				const auto& upgradeValue = m_currentRequest->getHeader(QtHttpHeader::Upgrade).toLower();
+				if (upgradeValue.compare(QByteArrayLiteral("websocket"), Qt::CaseInsensitive) == 0) {
+
+					qDebug() << "WebSocket upgrade detected, passing to QWebSocketServer";
+
 					if(m_websocketClient == Q_NULLPTR)
 					{
 						// disconnect this slot from socket for further requests
 						disconnect(m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived);
-						// disabling packet bunching
-						m_sockClient->setSocketOption(QAbstractSocket::LowDelayOption, 1);
-						m_sockClient->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
-						m_websocketClient = new WebSocketClient(m_currentRequest, m_sockClient, m_localConnection, this);
+						m_sockClient->rollbackTransaction();
+						m_websocketServer.handleConnection(m_sockClient);
+						emit m_sockClient->readyRead();
+						return;
 					}
 
 					break;
 				}
 
+				m_sockClient->commitTransaction();
 				// add  post data to request and catch /jsonrpc subroute url
 				if ( m_currentRequest->getCommand() == "POST")
 				{
@@ -227,6 +237,8 @@ void QtHttpClientWrapper::onClientDataReceived (void)
 			case ParsingError: // there was an error durin one of parsing steps
 			{
 				m_sockClient->readAll (); // clear remaining buffer to ignore content
+				m_sockClient->commitTransaction();
+
 				QtHttpReply reply (m_serverHandle);
 				reply.setStatusCode (QtHttpReply::BadRequest);
 				reply.appendRawData (QByteArrayLiteral ("<h1>Bad Request (HTTP parsing error) !</h1>"));
@@ -365,3 +377,19 @@ void QtHttpClientWrapper::closeConnection()
 	}
 	m_sockClient->close ();
 }
+
+void QtHttpClientWrapper::onNewWebSocketConnection() {
+
+	// Handle the pending connection
+	QWebSocket* webSocket = m_websocketServer.nextPendingConnection();
+	if (webSocket) {
+		qDebug() << "New WebSocket connection established";
+
+		// Manage the WebSocketJsonHandler for this connection
+		WebSocketJsonHandler* handler = new WebSocketJsonHandler(webSocket);
+		connect(webSocket, &QWebSocket::disconnected, handler, &QObject::deleteLater);
+	}
+	else {
+		qWarning() << "No pending WebSocket connection!";
+	}
+}
diff --git a/libsrc/webserver/QtHttpClientWrapper.h b/libsrc/webserver/QtHttpClientWrapper.h
index fd2d9f64..76632d2f 100644
--- a/libsrc/webserver/QtHttpClientWrapper.h
+++ b/libsrc/webserver/QtHttpClientWrapper.h
@@ -3,6 +3,8 @@
 
 #include <QObject>
 #include <QString>
+#include <QWebSocketServer>
+#include <QCoreApplication>
 
 class QTcpSocket;
 
@@ -39,8 +41,17 @@ public:
 	///
 	void closeConnection();
 
+	QWebSocketServer m_websocketServer{
+	QCoreApplication::applicationName() + QLatin1Char('/') + QCoreApplication::applicationVersion(),
+	QWebSocketServer::NonSecureMode
+	};
+
+signals:
+	void newWebSocketConnection();
+
 private slots:
 	void onClientDataReceived (void);
+	void onNewWebSocketConnection();
 
 protected:
 	ParsingStatus sendReplyToClient (QtHttpReply * reply);
diff --git a/libsrc/webserver/QtHttpServer.cpp b/libsrc/webserver/QtHttpServer.cpp
index ff8fe207..24e6b4b7 100644
--- a/libsrc/webserver/QtHttpServer.cpp
+++ b/libsrc/webserver/QtHttpServer.cpp
@@ -40,7 +40,7 @@ QtHttpServer::QtHttpServer (QObject * parent)
 	, m_netOrigin  (NetOrigin::getInstance())
 {
 	m_sockServer = new QtHttpServerWrapper (this);
-	connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected);
+	connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected, Qt::UniqueConnection);
 }
 
 void QtHttpServer::start (quint16 port)
@@ -81,27 +81,27 @@ void QtHttpServer::onClientConnected (void)
 	{
 		if (QTcpSocket * sock = m_sockServer->nextPendingConnection ())
 		{
-			if(m_netOrigin->accessAllowed(sock->peerAddress(), sock->localAddress()))
+			if (m_netOrigin->accessAllowed(sock->peerAddress(), sock->localAddress()))
 			{
-				connect (sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected);
+				connect(sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected);
 
 				if (m_useSsl)
 				{
-					if (QSslSocket * ssl = qobject_cast<QSslSocket *> (sock))
+					if (QSslSocket* ssl = qobject_cast<QSslSocket*> (sock))
 					{
-						connect (ssl, SslErrorSignal (&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors);
-						connect (ssl, &QSslSocket::encrypted,                  this, &QtHttpServer::onClientSslEncrypted);
-						connect (ssl, &QSslSocket::peerVerifyError,            this, &QtHttpServer::onClientSslPeerVerifyError);
-						connect (ssl, &QSslSocket::modeChanged,                this, &QtHttpServer::onClientSslModeChanged);
-						ssl->setLocalCertificateChain (m_sslCerts);
-						ssl->setPrivateKey (m_sslKey);
-						ssl->setPeerVerifyMode (QSslSocket::AutoVerifyPeer);
-						ssl->startServerEncryption ();
+						connect(ssl, SslErrorSignal(&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors);
+						connect(ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted);
+						connect(ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError);
+						connect(ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged);
+						ssl->setLocalCertificateChain(m_sslCerts);
+						ssl->setPrivateKey(m_sslKey);
+						ssl->setPeerVerifyMode(QSslSocket::AutoVerifyPeer);
+						ssl->startServerEncryption();
 					}
 				}
 
-				QtHttpClientWrapper * wrapper = new QtHttpClientWrapper (sock, m_netOrigin->isLocalAddress(sock->peerAddress(), sock->localAddress()), this);
-				m_socksClientsHash.insert (sock, wrapper);
+				QtHttpClientWrapper* wrapper = new QtHttpClientWrapper(sock, m_netOrigin->isLocalAddress(sock->peerAddress(), sock->localAddress()), this);
+				m_socksClientsHash.insert(sock, wrapper);
 				emit clientConnected (wrapper->getGuid ());
 			}
 			else
diff --git a/libsrc/webserver/WebSocketClient.cpp b/libsrc/webserver/WebSocketClient.cpp
deleted file mode 100644
index a4eab0e3..00000000
--- a/libsrc/webserver/WebSocketClient.cpp
+++ /dev/null
@@ -1,359 +0,0 @@
-#include "WebSocketClient.h"
-#include "QtHttpRequest.h"
-#include "QtHttpHeader.h"
-
-#include <hyperion/Hyperion.h>
-#include <api/JsonAPI.h>
-#include <api/JsonCallbacks.h>
-
-#include <QTcpSocket>
-#include <QtEndian>
-#include <QCryptographicHash>
-#include <QJsonObject>
-
-WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, bool localConnection, QObject* parent)
-	: QObject(parent)
-	, _socket(sock)
-	, _log(Logger::getInstance("WEBSOCKET"))
-{
-	// connect socket; disconnect handled from QtHttpServer
-	connect(_socket, &QTcpSocket::readyRead , this, &WebSocketClient::handleWebSocketFrame);
-
-	// QtHttpRequest contains all headers for handshake
-	QByteArray secWebSocketKey = request->getHeader(QtHttpHeader::SecWebSocketKey);
-	const QString client = request->getClientInfo().clientAddress.toString();
-
-	// Json processor
-	_jsonAPI.reset(new JsonAPI(client, _log, localConnection, this));
-	connect(_jsonAPI.get(), &JsonAPI::callbackReady, this, &WebSocketClient::sendMessage);
-	connect(_jsonAPI.get(), &JsonAPI::forceClose, this,[this]() { this->sendClose(CLOSECODE::NORMAL); });
-
-	connect(_jsonAPI->getCallBack().get(), &JsonCallbacks::callbackReady, this, &WebSocketClient::sendMessage);
-
-	connect(this, &WebSocketClient::handleMessage, _jsonAPI.get(), &JsonAPI::handleMessage);
-
-	Debug(_log, "New connection from %s", QSTRING_CSTR(client));
-
-	// do handshake
-	secWebSocketKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
-	QByteArray hash = QCryptographicHash::hash(secWebSocketKey, QCryptographicHash::Sha1).toBase64();
-
-	QString data
-		= QString("HTTP/1.1 101 Switching Protocols\r\n")
-		+ QString("Upgrade: websocket\r\n")
-		+ QString("Connection: Upgrade\r\n")
-		+ QString("Sec-WebSocket-Accept: ")+QString(hash.data()) + "\r\n\r\n";
-
-	_socket->write(QSTRING_CSTR(data), data.size());
-	_socket->flush();
-
-	// Init JsonAPI
-	_jsonAPI->initialize();
-}
-
-void WebSocketClient::handleWebSocketFrame()
-{
-	while (_socket->bytesAvailable())
-	{
-		// we are on no continious reading from socket from call before
-		if (!_notEnoughData)
-		{
-			getWsFrameHeader(&_wsh);
-		}
-
-		if(_socket->bytesAvailable() < (qint64)_wsh.payloadLength)
-		{
-			_notEnoughData=true;
-			return;
-		}
-		_notEnoughData = false;
-
-		QByteArray buf = _socket->read(_wsh.payloadLength);
-
-		if (OPCODE::invalid((OPCODE::value)_wsh.opCode))
-		{
-			sendClose(CLOSECODE::INV_TYPE, "invalid opcode");
-			return;
-		}
-
-		// check the type of data frame
-		bool isContinuation=false;
-
-		switch (_wsh.opCode)
-		{
-			case OPCODE::CONTINUATION:
-				isContinuation = true;
-				// no break here, just jump over to opcode text
-
-			case OPCODE::BINARY:
-			case OPCODE::TEXT:
-			{
-				// A fragmented message consists of a single frame with the FIN bit
-				// clear and an opcode other than 0, followed by zero or more frames
-				// with the FIN bit clear and the opcode set to 0, and terminated by
-				// a single frame with the FIN bit set and an opcode of 0.
-				//
-				// Store frame type given by first frame
-				if (_wsh.opCode != OPCODE::CONTINUATION )
-				{
-					_frameOpCode = _wsh.opCode;
-				}
-
-				// check for protocol violations
-				if (_onContinuation && !isContinuation)
-				{
-					sendClose(CLOSECODE::VIOLATION, "protocol violation, somebody sends frames in between continued frames");
-					return;
-				}
-
-				if (!_wsh.masked && _wsh.opCode == OPCODE::TEXT)
-				{
-					sendClose(CLOSECODE::VIOLATION, "protocol violation, unmasked text frames not allowed");
-					return;
-				}
-
-				// unmask data
-				for (int i=0; i < buf.size(); i++)
-				{
-					buf[i] = buf[i] ^ _wsh.key[i % 4];
-				}
-
-				_onContinuation = !_wsh.fin || isContinuation;
-
-				// frame contains text, extract it, append data if this is a continuation
-				if (_wsh.fin && ! isContinuation) // one frame
-				{
-					_wsReceiveBuffer.clear();
-				}
-				_wsReceiveBuffer.append(buf);
-
-				// this is the final frame, decode and handle data
-				if (_wsh.fin)
-				{
-					_onContinuation = false;
-
-					if (_frameOpCode == OPCODE::TEXT)
-					{
-						emit handleMessage(QString(_wsReceiveBuffer),"");
-					}
-					else
-					{
-						handleBinaryMessage(_wsReceiveBuffer);
-					}
-					_wsReceiveBuffer.clear();
-
-				}
-			}
-			break;
-
-			case OPCODE::CLOSE:
-				{
-					sendClose(CLOSECODE::NORMAL);
-				}
-				break;
-
-			case OPCODE::PING:
-				{
-					// ping received, send pong
-					quint8 pong[] = {OPCODE::PONG, 0};
-					_socket->write((const char*)pong, 2);
-					_socket->flush();
-				}
-				break;
-
-			case OPCODE::PONG:
-				{
-					Error(_log, "pong received, protocol violation!");
-				}
-
-			default:
-				Warning(_log, "strange %d\n%s\n",  _wsh.opCode, QSTRING_CSTR(QString(buf)));
-		}
-	}
-}
-
-void WebSocketClient::getWsFrameHeader(WebSocketHeader* header)
-{
-	char fin_rsv_opcode, mask_length;
-	_socket->getChar(&fin_rsv_opcode);
-	_socket->getChar(&mask_length);
-
-	header->fin    = (fin_rsv_opcode & BHB0_FIN) == BHB0_FIN;
-	header->opCode = fin_rsv_opcode  & BHB0_OPCODE;
-	header->masked = (mask_length & BHB1_MASK) == BHB1_MASK;
-	header->payloadLength = mask_length  & BHB1_PAYLOAD;
-
-	// get size of payload
-	switch (header->payloadLength)
-	{
-		case payload_size_code_16bit:
-		{
-			QByteArray buf = _socket->read(2);
-			header->payloadLength = ((buf.at(0) << 8) & 0xFF00) | (buf.at(1) & 0xFF);
-		}
-		break;
-
-		case payload_size_code_64bit:
-		{
-			QByteArray buf = _socket->read(8);
-			header->payloadLength = 0;
-			for (uint i=0; i < 8; i++)
-			{
-				header->payloadLength |= ((quint64)(buf.at(i) & 0xFF)) << (8*(7-i));
-			}
-		}
-		break;
-	}
-
-	// if the data is masked we need to get the key for unmasking
-	if (header->masked)
-	{
-		_socket->read(header->key, 4);
-	}
-}
-
-/// See http://tools.ietf.org/html/rfc6455#section-5.2 for more information
-void WebSocketClient::sendClose(int status, const QString& reason)
-{
-	Debug(_log, "Send close to %s: %d %s", QSTRING_CSTR(_socket->peerAddress().toString()), status, QSTRING_CSTR(reason));
-	ErrorIf(!reason.isEmpty(), _log, "%s", QSTRING_CSTR(reason));
-	_receiveBuffer.clear();
-	QByteArray sendBuffer;
-
-	sendBuffer.append(136+(status-1000));
-	int length = reason.size();
-	if(length >= 126)
-	{
-		sendBuffer.append( (length > 0xffff) ? 127 : 126);
-		int num_bytes = (length > 0xffff) ? 8 : 2;
-
-		for(int c = num_bytes - 1; c != -1; c--)
-		{
-			sendBuffer.append( quint8((static_cast<unsigned long long>(length) >> (8 * c)) % 256));
-		}
-	}
-	else
-	{
-		sendBuffer.append(quint8(length));
-	}
-
-	sendBuffer.append(reason.toUtf8());
-
-	_socket->write(sendBuffer);
-	_socket->flush();
-	_socket->close();
-}
-
-
-void WebSocketClient::handleBinaryMessage(QByteArray &data)
-{
-	unsigned imgSize    = data.size() - 4;
-	unsigned width      = ((data.at(2) << 8) & 0xFF00) | (data.at(3) & 0xFF);
-	unsigned height     =  imgSize / width;
-
-	if ( imgSize % width > 0 )
-	{
-		Error(_log, "data size is not multiple of width");
-		return;
-	}
-
-	Image<ColorRgb> image;
-	image.resize(width, height);
-
-	memcpy(image.memptr(), data.data()+4, imgSize);
-}
-
-
-qint64 WebSocketClient::sendMessage(QJsonObject obj)
-{
-	QJsonDocument writer(obj);
-	QByteArray data = writer.toJson(QJsonDocument::Compact) + "\n";
-
-	if (!_socket || (_socket->state() != QAbstractSocket::ConnectedState)) return 0;
-
-	qint64 payloadWritten = 0;
-	quint32 payloadSize   = data.size();
-	const char * payload  = data.data();
-
-	qint32 numFrames = payloadSize / FRAME_SIZE_IN_BYTES + ((quint64(payloadSize) % FRAME_SIZE_IN_BYTES) > 0 ? 1 : 0);
-
-	for (int i = 0; i < numFrames; ++i)
-	{
-		const bool isLastFrame = (i == (numFrames - 1));
-
-		quint64 position  = i * FRAME_SIZE_IN_BYTES;
-		quint32 frameSize = (payloadSize-position >= FRAME_SIZE_IN_BYTES) ? FRAME_SIZE_IN_BYTES : (payloadSize-position);
-
-		QByteArray buf = makeFrameHeader((i == 0) ? OPCODE::TEXT : OPCODE::CONTINUATION, frameSize, isLastFrame);
-		sendMessage_Raw(buf);
-
-		qint64 written = sendMessage_Raw(payload+position,frameSize);
-		if (written > 0)
-		{
-			payloadWritten += written;
-		}
-		else
-		{
-			_socket->flush();
-			Error(_log, "Error writing bytes to socket: %s", QSTRING_CSTR(_socket->errorString()));
-			break;
-		}
-	}
-
-	if (payloadSize != payloadWritten)
-	{
-		Error(_log, "Error writing bytes to socket %d bytes from %d written", payloadWritten, payloadSize);
-		return -1;
-	}
-	return payloadWritten;
-}
-
-qint64 WebSocketClient::sendMessage_Raw(const char* data, quint64 size)
-{
-	return _socket->write(data, size);
-}
-
-qint64 WebSocketClient::sendMessage_Raw(QByteArray &data)
-{
-	return _socket->write(data.data(), data.size());
-}
-
-
-QByteArray WebSocketClient::makeFrameHeader(quint8 opCode, quint64 payloadLength, bool lastFrame)
-{
-	QByteArray header;
-
-	if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL)
-	{
-		//FIN, RSV1-3, opcode (RSV-1, RSV-2 and RSV-3 are zero)
-		quint8 byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00));
-		header.append(static_cast<char>(byte));
-
-		byte = 0x00;
-		if (payloadLength <= 125)
-		{
-			byte |= static_cast<quint8>(payloadLength);
-			header.append(static_cast<char>(byte));
-		}
-		else if (payloadLength <= 0xFFFFU)
-		{
-			byte |= 126;
-			header.append(static_cast<char>(byte));
-			quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
-			header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
-		}
-		else
-		{
-			byte |= 127;
-			header.append(static_cast<char>(byte));
-			quint64 swapped = qToBigEndian<quint64>(payloadLength);
-			header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
-		}
-	}
-	else
-	{
-		Error(_log, "Payload too big!");
-	}
-
-	return header;
-}
diff --git a/libsrc/webserver/WebSocketClient.h b/libsrc/webserver/WebSocketClient.h
deleted file mode 100644
index 520cefdb..00000000
--- a/libsrc/webserver/WebSocketClient.h
+++ /dev/null
@@ -1,82 +0,0 @@
-#pragma once
-
-#include <utils/Logger.h>
-#include "WebSocketUtils.h"
-#include <api/JsonAPI.h>
-
-#include <QScopedPointer>
-
-class QTcpSocket;
-
-class QtHttpRequest;
-class Hyperion;
-class JsonAPI;
-
-class WebSocketClient : public QObject
-{
-	Q_OBJECT
-public:
-	WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, bool localConnection, QObject* parent);
-
-	struct WebSocketHeader
-	{
-		bool          fin;
-		quint8        opCode;
-		bool          masked;
-		quint64       payloadLength;
-		char          key[4];
-	};
-
-private:
-	QTcpSocket* _socket;
-	Logger* _log;
-	Hyperion* _hyperion;
-	QScopedPointer<JsonAPI> _jsonAPI;
-
-	void getWsFrameHeader(WebSocketHeader* header);
-	void sendClose(int status, const QString& reason = "");
-	void handleBinaryMessage(QByteArray &data);
-	qint64 sendMessage_Raw(const char* data, quint64 size);
-	qint64 sendMessage_Raw(QByteArray &data);
-	QByteArray makeFrameHeader(quint8 opCode, quint64 payloadLength, bool lastFrame);
-
-	/// The buffer used for reading data from the socket
-	QByteArray _receiveBuffer;
-
-	/// buffer for websockets multi frame receive
-	QByteArray _wsReceiveBuffer;
-	quint8 _maskKey[4];
-
-	bool _onContinuation = false;
-
-	// true when data is missing for parsing
-	bool _notEnoughData = false;
-
-	// websocket header store
-	WebSocketHeader _wsh;
-
-	//opCode of first frame (in case of fragmented frames)
-	quint8 _frameOpCode;
-
-	// masks for fields in the basic header
-	static uint8_t const BHB0_OPCODE = 0x0F;
-	static uint8_t const BHB0_RSV3   = 0x10;
-	static uint8_t const BHB0_RSV2   = 0x20;
-	static uint8_t const BHB0_RSV1   = 0x40;
-	static uint8_t const BHB0_FIN    = 0x80;
-
-	static uint8_t const BHB1_PAYLOAD = 0x7F;
-	static uint8_t const BHB1_MASK    = 0x80;
-
-	static uint8_t const payload_size_code_16bit = 0x7E; // 126
-	static uint8_t const payload_size_code_64bit = 0x7F; // 127
-
-	static const quint64 FRAME_SIZE_IN_BYTES = 512 * 512 * 2;  //maximum size of a frame when sending a message
-
-private slots:
-	void handleWebSocketFrame();
-	qint64 sendMessage(QJsonObject obj);
-
-signals:
-	void handleMessage(const QString &message, const QString &httpAuthHeader);
-};
diff --git a/libsrc/webserver/WebSocketJsonHandler.cpp b/libsrc/webserver/WebSocketJsonHandler.cpp
new file mode 100644
index 00000000..b0c8beb1
--- /dev/null
+++ b/libsrc/webserver/WebSocketJsonHandler.cpp
@@ -0,0 +1,49 @@
+#include "WebSocketJsonHandler.h"
+
+#include <api/JsonAPI.h>
+#include <api/JsonCallbacks.h>
+#include <utils/JsonUtils.h>
+#include <utils/NetOrigin.h>
+
+WebSocketJsonHandler::WebSocketJsonHandler(QWebSocket* websocket, QObject* parent)
+	: QObject(parent)
+	, _websocket(websocket)
+	, _log(Logger::getInstance("WEBSOCKET"))
+{
+	connect(_websocket, &QWebSocket::textMessageReceived, this, &WebSocketJsonHandler::onTextMessageReceived);
+	connect(_websocket, &QWebSocket::disconnected, this, &WebSocketJsonHandler::onDisconnected);
+
+	const QString client = _websocket->peerAddress().toString();
+	Debug(_log, "New WebSocket connection from %s", QSTRING_CSTR(client));
+
+	bool localConnection = NetOrigin::getInstance()->isLocalAddress(_websocket->peerAddress(), _websocket->localAddress());
+
+	// Json processor
+	_jsonAPI.reset(new JsonAPI(client, _log, localConnection, this));
+
+	connect(_jsonAPI.get(), &JsonAPI::callbackReady, this, &WebSocketJsonHandler::sendMessage);
+	connect(_jsonAPI->getCallBack().get(), &JsonCallbacks::callbackReady, this, &WebSocketJsonHandler::sendMessage);
+
+	// Init JsonAPI
+	_jsonAPI->initialize();
+}
+
+void WebSocketJsonHandler::onTextMessageReceived(const QString& message)
+{
+	qDebug() << "WebSocket message received:" << message;
+	_jsonAPI.get()->handleMessage(message);
+}
+
+qint64 WebSocketJsonHandler::sendMessage(QJsonObject obj)
+{
+	QString const message = JsonUtils::jsonValueToQString(obj);
+	qDebug() << "WebSocket send message: " << message;
+	return _websocket->sendTextMessage(message);
+}
+
+void WebSocketJsonHandler::onDisconnected()
+{
+	qDebug() << "WebSocket disconnected";
+}
+
+
diff --git a/libsrc/webserver/WebSocketJsonHandler.h b/libsrc/webserver/WebSocketJsonHandler.h
new file mode 100644
index 00000000..40c3f59d
--- /dev/null
+++ b/libsrc/webserver/WebSocketJsonHandler.h
@@ -0,0 +1,30 @@
+#ifndef WEBSOCKETJSONHANDLER_H
+#define WEBSOCKETJSONHANDLER_H
+
+#include <utils/Logger.h>
+#include <api/JsonAPI.h>
+
+#include <QObject>
+#include <QWebSocket>
+#include <QScopedPointer>
+
+class WebSocketJsonHandler : public QObject
+{
+	Q_OBJECT
+
+public:
+	WebSocketJsonHandler(QWebSocket* websocket, QObject* parent = nullptr);
+
+private slots:
+	void onTextMessageReceived(const QString& message);
+	void onDisconnected();
+	qint64 sendMessage(QJsonObject obj);
+
+private:
+	QWebSocket* _websocket;
+
+	Logger* _log;
+	QScopedPointer<JsonAPI> _jsonAPI;
+};
+
+#endif // WEBSOCKETJSONHANDLER_H
diff --git a/libsrc/webserver/WebSocketUtils.h b/libsrc/webserver/WebSocketUtils.h
deleted file mode 100644
index f490ae97..00000000
--- a/libsrc/webserver/WebSocketUtils.h
+++ /dev/null
@@ -1,75 +0,0 @@
-#pragma once
-
-/// Constants and utility functions related to WebSocket opcodes
-/**
- * WebSocket Opcodes are 4 bits. See RFC6455 section 5.2.
- */
-namespace OPCODE
-{
-	enum value
-	{
-		CONTINUATION = 0x0,
-		TEXT = 0x1,
-		BINARY = 0x2,
-		RSV3 = 0x3,
-		RSV4 = 0x4,
-		RSV5 = 0x5,
-		RSV6 = 0x6,
-		RSV7 = 0x7,
-		CLOSE = 0x8,
-		PING = 0x9,
-		PONG = 0xA,
-		CONTROL_RSVB = 0xB,
-		CONTROL_RSVC = 0xC,
-		CONTROL_RSVD = 0xD,
-		CONTROL_RSVE = 0xE,
-		CONTROL_RSVF = 0xF
-	};
-
-	/// Check if an opcode is reserved
-	/**
-	 * @param v The opcode to test.
-	 * @return Whether or not the opcode is reserved.
-	 */
-	inline bool reserved(value v)
-	{
-		return (v >= RSV3 && v <= RSV7) || (v >= CONTROL_RSVB && v <= CONTROL_RSVF);
-	}
-
-	/// Check if an opcode is invalid
-	/**
-	 * Invalid opcodes are negative or require greater than 4 bits to store.
-	 *
-	 * @param v The opcode to test.
-	 * @return Whether or not the opcode is invalid.
-	 */
-	inline bool invalid(value v)
-	{
-		return (v > 0xF || v < 0);
-	}
-
-	/// Check if an opcode is for a control frame
-	/**
-	 * @param v The opcode to test.
-	 * @return Whether or not the opcode is a control opcode.
-	*/
-	inline bool is_control(value v)
-	{
-		return v >= 0x8;
-	}
-}
-
-namespace CLOSECODE
-{
-	enum value
-	{
-		NORMAL    = 1000,
-		AWAY      = 1001,
-		TERM      = 1002,
-		INV_TYPE  = 1003,
-		INV_DATA  = 1007,
-		VIOLATION = 1008,
-		BIG_MSG   = 1009,
-		UNEXPECTED= 1011
-	};
-}