summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md40
-rw-r--r--examples/qmlwebsocketclient/qml/qmlwebsocketclient/main.qml39
-rw-r--r--examples/qmlwebsocketclient/qmlwebsocketclient.pro8
-rw-r--r--src/imports/qmlwebsockets/plugins.qmltypes46
-rw-r--r--src/imports/qmlwebsockets/qmldir4
-rw-r--r--src/imports/qmlwebsockets/qmlwebsockets.pro14
-rw-r--r--src/imports/qmlwebsockets/qmlwebsockets_plugin.cpp10
-rw-r--r--src/imports/qmlwebsockets/qqmlwebsocket.cpp162
-rw-r--r--src/imports/qmlwebsockets/qqmlwebsocket.h55
9 files changed, 333 insertions, 45 deletions
diff --git a/README.md b/README.md
index 3d479c9..b164a22 100644
--- a/README.md
+++ b/README.md
@@ -1,38 +1,36 @@
### Introduction
-QWebSockets is a pure Qt implementation of WebSockets - both client and server.
-It is implemented as a Qt source code module (.pri file), that can easily be embedded into existing Qt projects. It has no other dependencies that Qt.
+`QtWebSockets` is a pure Qt implementation of WebSockets - both client and server.
+It is implemented as a Qt add-on module, that can easily be embedded into existing Qt projects. It has no other dependencies than Qt.
### Features
+* Client and server capable
* Text and binary sockets
* Frame-based and message-based signals
-* Works through proxies
* Strict Unicode checking
-
-### Restrictions
-Non-characters (according [Unicode Standard 6.2](http://www.unicode.org/versions/Unicode6.2.0/)) are rejected by QWebSockets, **even if the UTF-8 sequence is valid**.
-##### Rationale
-The WebSocket specification is talking about _Valid UTF-8 codes and sequences_. Strictly speaking, UTF-xx encodings are reversible. That means, that the [66 non-character codes](http://www.unicode.org/faq/private_use.html#noncharacters) (including `U+FFFE` and `U+FFFF`), are valid UTF-8, and hence are perfectly acceptable within WebSocket text messages.
-According to the Unicode standard, they SHOULD NOT be used in information interchange, but a [recent corrigendum](http://www.unicode.org/versions/corrigendum9.html) clarifies that non-characters CAN be exchanged.
-However, non-characters are for internal use, and hence, they are implementation specific (e.g. non-characters can be used to carry meta-information). _They have to be interpreted._
-When used with `QString`, they are replaced with `U+FFFD - REPLACEMENT CHARACTER`, and rendered - non-standard - as a question mark (this is the `QString` rendering of the non-character `U+FFFD`: �). Browsers keep the control characters untouched (this is the browser rendering of the non-character `U+FDD0`: ).
-
-With QWebSockets, text messages are just that: a collection of human-readable characters. Text messages never have to be interpreted to be rendered correctly. In case, you still want to do implementation specific trickery, use binary messages instead. Indeed, if non-characters were allowed in text messages, then every text message has to be parsed, character-per-character, to find out if it contains special control codes, or a protocol should be devised that indicates whether the message contains that kind of control codes. We keep it simple: text is text and nothing more.
+* WSS and proxy support
### Requirements
Qt 5.x
-### Usage
-Include the .pri file into your Qt project
-~~~
-include (qwebsockets.pri)
-~~~
+### Build And Usage
+Checkout the source code from gitorious
+Go into the source directory and execute:
+
+ qmake
+ make
+ make install
+
+
+The last command will install `QtWebSockets` as a Qt module.
+
+To use, add `websockets` to the QT variable.
+
+`QT += websockets`
### Compliance
-QWebSockets is compliant with [RFC6455](http://datatracker.ietf.org/doc/rfc6455/?include_text=1) and has been tested with the [Autobahn Testsuite](http://autobahn.ws/testsuite).
-Only tests with **Unicode non-characters** do **not** pass from the Autobahn Testsuite (see [Restrictions](#Restrictions)).
+`QtWebSockets` is compliant with [RFC6455](http://datatracker.ietf.org/doc/rfc6455/?include_text=1) and has been tested with the [Autobahn Testsuite](http://autobahn.ws/testsuite).
### Missing Features
-* WSS protocol
* Extensions and sub-protocols
### License
diff --git a/examples/qmlwebsocketclient/qml/qmlwebsocketclient/main.qml b/examples/qmlwebsocketclient/qml/qmlwebsocketclient/main.qml
index c038788..de3c89e 100644
--- a/examples/qmlwebsocketclient/qml/qmlwebsocketclient/main.qml
+++ b/examples/qmlwebsocketclient/qml/qmlwebsocketclient/main.qml
@@ -38,27 +38,56 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-
import QtQuick 2.0
-import Qt.Playground.WebSockets 1.0
+import Qt.WebSockets 1.0
Rectangle {
width: 360
height: 360
WebSocket {
-
+ id: socket
+ url: "ws://echo.websocket.org"
+ onTextMessageReceived: {
+ messageBox.text = messageBox.text + "\nReceived message: " + message
+ }
+ onStatusChanged: if (socket.status == WebSocket.Error) {
+ console.log("Error: " + socket.errorString)
+ } else if (socket.status == WebSocket.Open) {
+ socket.sendTextMessage("Hello World")
+ } else if (socket.status == WebSocket.Closed) {
+ messageBox.text += "\nSocket closed"
+ }
+ active: false
}
+ WebSocket {
+ id: secureWebSocket
+ url: "wss://echo.websocket.org"
+ onTextMessageReceived: {
+ messageBox.text = messageBox.text + "\nReceived secure message: " + message
+ }
+ onStatusChanged: if (secureWebSocket.status == WebSocket.Error) {
+ console.log("Error: " + secureWebSocket.errorString)
+ } else if (secureWebSocket.status == WebSocket.Open) {
+ secureWebSocket.sendTextMessage("Hello Secure World")
+ } else if (secureWebSocket.status == WebSocket.Closed) {
+ messageBox.text += "\nSecure socket closed"
+ }
+ active: false
+ }
Text {
- text: qsTr("Hello World")
+ id: messageBox
+ text: socket.status == WebSocket.Open ? qsTr("Sending...") : qsTr("Welcome!")
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
- Qt.quit();
+ socket.active = !socket.active
+ secureWebSocket.active = !secureWebSocket.active;
+ //Qt.quit();
}
}
}
diff --git a/examples/qmlwebsocketclient/qmlwebsocketclient.pro b/examples/qmlwebsocketclient/qmlwebsocketclient.pro
index e4a7d13..e7dc82a 100644
--- a/examples/qmlwebsocketclient/qmlwebsocketclient.pro
+++ b/examples/qmlwebsocketclient/qmlwebsocketclient.pro
@@ -1,11 +1,13 @@
-QT += qml quick
+QT += core qml quick websockets
TARGET = qmlwebsocketclient
+TEMPLATE = app
+
CONFIG -= app_bundle
SOURCES += main.cpp
-RESOURCES += \
- data.qrc
+RESOURCES += data.qrc
+OTHER_FILES += qml/qmlwebsocketclient/main.qml
diff --git a/src/imports/qmlwebsockets/plugins.qmltypes b/src/imports/qmlwebsockets/plugins.qmltypes
new file mode 100644
index 0000000..8116fce
--- /dev/null
+++ b/src/imports/qmlwebsockets/plugins.qmltypes
@@ -0,0 +1,46 @@
+import QtQuick.tooling 1.1
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by:
+// 'qmlplugindump -notrelocatable Qt.WebSockets 1.0'
+
+Module {
+ Component {
+ name: "QQmlWebSocket"
+ prototype: "QObject"
+ exports: ["Qt.WebSockets/WebSocket 1.0"]
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "Status"
+ values: {
+ "Connecting": 0,
+ "Open": 1,
+ "Closing": 2,
+ "Closed": 3,
+ "Error": 4
+ }
+ }
+ Property { name: "url"; type: "QUrl" }
+ Property { name: "status"; type: "Status"; isReadonly: true }
+ Property { name: "errorString"; type: "string"; isReadonly: true }
+ Property { name: "active"; type: "bool" }
+ Signal {
+ name: "textMessageReceived"
+ Parameter { name: "message"; type: "string" }
+ }
+ Signal {
+ name: "statusChanged"
+ Parameter { name: "status"; type: "Status" }
+ }
+ Signal {
+ name: "activeChanged"
+ Parameter { name: "isActive"; type: "bool" }
+ }
+ Method {
+ name: "sendTextMessage"
+ Parameter { name: "message"; type: "string" }
+ }
+ }
+}
diff --git a/src/imports/qmlwebsockets/qmldir b/src/imports/qmlwebsockets/qmldir
index 549c286..4ae1035 100644
--- a/src/imports/qmlwebsockets/qmldir
+++ b/src/imports/qmlwebsockets/qmldir
@@ -1,3 +1,3 @@
-module Qt.Playground.WebSockets
-
+module Qt.WebSockets
plugin declarative_qmlwebsockets
+typeinfo plugins.qmltypes
diff --git a/src/imports/qmlwebsockets/qmlwebsockets.pro b/src/imports/qmlwebsockets/qmlwebsockets.pro
index 18d3713..2d3353e 100644
--- a/src/imports/qmlwebsockets/qmlwebsockets.pro
+++ b/src/imports/qmlwebsockets/qmlwebsockets.pro
@@ -1,11 +1,15 @@
QT = core websockets qml
-TARGETPATH = Qt/Playground/WebSockets
+TARGETPATH = Qt/WebSockets
-HEADERS += qmlwebsockets_plugin.h \
- qqmlwebsocket.h
+HEADERS += qmlwebsockets_plugin.h \
+ qqmlwebsocket.h
-SOURCES += qmlwebsockets_plugin.cpp \
- qqmlwebsocket.cpp
+SOURCES += qmlwebsockets_plugin.cpp \
+ qqmlwebsocket.cpp
+
+OTHER_FILES += qmldir
+
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
load(qml_plugin)
diff --git a/src/imports/qmlwebsockets/qmlwebsockets_plugin.cpp b/src/imports/qmlwebsockets/qmlwebsockets_plugin.cpp
index ea2d1b3..c8d2cd6 100644
--- a/src/imports/qmlwebsockets/qmlwebsockets_plugin.cpp
+++ b/src/imports/qmlwebsockets/qmlwebsockets_plugin.cpp
@@ -45,12 +45,8 @@
void QmlWebsocket_plugin::registerTypes(const char *uri)
{
- Q_ASSERT(uri == QLatin1String("Qt.Playground.WebSockets"));
+ Q_ASSERT(uri == QLatin1String("Qt.WebSockets"));
- int major = 1;
- int minor = 0;
-
- // @uri Qt.Playground.WebSockets
-
- qmlRegisterType<QQmlWebSocket>(uri, major, minor, "WebSocket");
+ // @uri Qt.WebSockets
+ qmlRegisterType<QQmlWebSocket>(uri, 1 /*major*/, 0 /*minor*/, "WebSocket");
}
diff --git a/src/imports/qmlwebsockets/qqmlwebsocket.cpp b/src/imports/qmlwebsockets/qqmlwebsocket.cpp
index 8858f24..49b1dfd 100644
--- a/src/imports/qmlwebsockets/qqmlwebsocket.cpp
+++ b/src/imports/qmlwebsockets/qqmlwebsocket.cpp
@@ -40,18 +40,176 @@
****************************************************************************/
#include "qqmlwebsocket.h"
+#include <QtWebSockets/QWebSocket>
QQmlWebSocket::QQmlWebSocket(QObject *parent) :
- QObject(parent)
+ QObject(parent),
+ m_webSocket(),
+ m_status(Closed),
+ m_url(),
+ m_isActive(false),
+ m_componentCompleted(true),
+ m_errorString()
{
}
-void QQmlWebSocket::classBegin()
+QQmlWebSocket::~QQmlWebSocket()
+{
+}
+
+void QQmlWebSocket::sendTextMessage(const QString &message)
+{
+ if (m_status != Open) {
+ setErrorString(tr("Messages can only be send when the socket has Open status."));
+ setStatus(Error);
+ return;
+ }
+ m_webSocket->write(message);
+}
+
+QUrl QQmlWebSocket::url() const
+{
+ return m_url;
+}
+
+void QQmlWebSocket::setUrl(const QUrl &url)
+{
+ if (m_url == url) {
+ return;
+ }
+ if (m_webSocket && (m_status == Open)) {
+ m_webSocket->close();
+ }
+ m_url = url;
+ Q_EMIT urlChanged();
+ if (m_webSocket) {
+ m_webSocket->open(m_url);
+ }
+}
+
+QQmlWebSocket::Status QQmlWebSocket::status() const
{
+ return m_status;
+}
+QString QQmlWebSocket::errorString() const
+{
+ return m_errorString;
+}
+
+void QQmlWebSocket::classBegin()
+{
+ m_componentCompleted = false;
+ m_errorString = tr("QQmlWebSocket is not ready.");
+ m_status = Closed;
}
void QQmlWebSocket::componentComplete()
{
+ m_webSocket.reset(new QWebSocket());
+ connect(m_webSocket.data(), SIGNAL(textMessageReceived(QString)), this, SIGNAL(textMessageReceived(QString)));
+ connect(m_webSocket.data(), SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
+ connect(m_webSocket.data(), SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onStateChanged(QAbstractSocket::SocketState)));
+
+ m_componentCompleted = true;
+
+ open();
+}
+
+void QQmlWebSocket::onError(QAbstractSocket::SocketError error)
+{
+ Q_UNUSED(error)
+ setErrorString(m_webSocket->errorString());
+ setStatus(Error);
+}
+
+void QQmlWebSocket::onStateChanged(QAbstractSocket::SocketState state)
+{
+ switch (state)
+ {
+ case QAbstractSocket::ConnectingState:
+ case QAbstractSocket::BoundState:
+ case QAbstractSocket::HostLookupState:
+ {
+ setStatus(Connecting);
+ break;
+ }
+ case QAbstractSocket::UnconnectedState:
+ {
+ setStatus(Closed);
+ break;
+ }
+ case QAbstractSocket::ConnectedState:
+ {
+ setStatus(Open);
+ break;
+ }
+ case QAbstractSocket::ClosingState:
+ {
+ setStatus(Closing);
+ break;
+ }
+ default:
+ {
+ setStatus(Connecting);
+ break;
+ }
+ }
+}
+
+void QQmlWebSocket::setStatus(QQmlWebSocket::Status status)
+{
+ if (m_status == status) {
+ return;
+ }
+ m_status = status;
+ if (status != Error) {
+ setErrorString();
+ }
+ Q_EMIT statusChanged(m_status);
+}
+
+void QQmlWebSocket::setActive(bool active)
+{
+ if (m_isActive == active) {
+ return;
+ }
+ m_isActive = active;
+ Q_EMIT activeChanged(m_isActive);
+ if (!m_componentCompleted) {
+ return;
+ }
+ if (m_isActive) {
+ open();
+ } else {
+ close();
+ }
+}
+
+bool QQmlWebSocket::isActive() const
+{
+ return m_isActive;
+}
+void QQmlWebSocket::open()
+{
+ if (m_componentCompleted && m_isActive && m_url.isValid() && m_webSocket) {
+ m_webSocket->open(m_url);
+ }
+}
+
+void QQmlWebSocket::close()
+{
+ if (m_componentCompleted && m_webSocket) {
+ m_webSocket->close();
+ }
+}
+
+void QQmlWebSocket::setErrorString(QString errorString)
+{
+ if (m_errorString == errorString) {
+ return;
+ }
+ m_errorString = errorString;
+ Q_EMIT errorStringChanged(m_errorString);
}
diff --git a/src/imports/qmlwebsockets/qqmlwebsocket.h b/src/imports/qmlwebsockets/qqmlwebsocket.h
index 20718c3..47cf6fa 100644
--- a/src/imports/qmlwebsockets/qqmlwebsocket.h
+++ b/src/imports/qmlwebsockets/qqmlwebsocket.h
@@ -44,6 +44,9 @@
#include <QObject>
#include <QQmlParserStatus>
+#include <QtQml>
+#include <QScopedPointer>
+#include <QWebSocket>
class QQmlWebSocket : public QObject, public QQmlParserStatus
{
@@ -51,12 +54,64 @@ class QQmlWebSocket : public QObject, public QQmlParserStatus
Q_DISABLE_COPY(QQmlWebSocket)
Q_INTERFACES(QQmlParserStatus)
+ Q_ENUMS(Status)
+ Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged)
+ Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged)
+ Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged)
+
public:
explicit QQmlWebSocket(QObject *parent = Q_NULLPTR);
+ virtual ~QQmlWebSocket();
+
+ enum Status
+ {
+ Connecting = 0,
+ Open = 1,
+ Closing = 2,
+ Closed = 3,
+ Error = 4
+ };
+
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+ Status status() const;
+ QString errorString() const;
+
+ void setActive(bool active);
+ bool isActive() const;
+
+public Q_SLOTS:
+ void sendTextMessage(const QString &message);
+
+
+Q_SIGNALS:
+ void textMessageReceived(QString message);
+ void statusChanged(Status status);
+ void activeChanged(bool isActive);
+ void errorStringChanged(QString errorString);
+ void urlChanged();
public:
void classBegin() Q_DECL_OVERRIDE;
void componentComplete() Q_DECL_OVERRIDE;
+
+private Q_SLOTS:
+ void onError(QAbstractSocket::SocketError error);
+ void onStateChanged(QAbstractSocket::SocketState state);
+
+private:
+ QScopedPointer<QWebSocket> m_webSocket;
+ Status m_status;
+ QUrl m_url;
+ bool m_isActive;
+ bool m_componentCompleted;
+ QString m_errorString;
+
+ void setStatus(Status status);
+ void open();
+ void close();
+ void setErrorString(QString errorString = QString());
};
#endif // QQMLWEBSOCKET_H