Browse Source

http transport for ethereum.js

cl-refactor
arkpar 10 years ago
parent
commit
d2a424b0e5
  1. 13
      libqwebthree/QWebThree.cpp
  2. 13
      mix/AppContext.cpp
  3. 9
      mix/AppContext.h
  4. 5
      mix/ClientModel.cpp
  5. 7
      mix/ClientModel.h
  6. 170
      mix/HttpServer.cpp
  7. 123
      mix/HttpServer.h
  8. 31
      mix/qml/WebPreview.qml
  9. 2
      mix/qml/html/WebContainer.html

13
libqwebthree/QWebThree.cpp

@ -41,20 +41,9 @@ void QWebThree::clientDieing()
this->disconnect(); this->disconnect();
} }
static QString formatInput(QJsonObject const& _object)
{
QJsonObject res;
res["jsonrpc"] = QString::fromStdString("2.0");
res["method"] = _object["call"];
res["params"] = _object["args"];
res["id"] = _object["_id"];
return QString::fromUtf8(QJsonDocument(res).toJson());
}
QString QWebThree::callMethod(QString _json) QString QWebThree::callMethod(QString _json)
{ {
QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object(); emit processData(_json, ""); // it's synchronous
emit processData(formatInput(f), ""); // it's synchronous
return m_response; return m_response;
} }

13
mix/AppContext.cpp

@ -33,7 +33,7 @@
#include "Exceptions.h" #include "Exceptions.h"
#include "AppContext.h" #include "AppContext.h"
#include "QEther.h" #include "QEther.h"
#include <libwebthree/WebThree.h> #include "HttpServer.h"
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
@ -44,19 +44,9 @@ const QString c_projectFileName = "project.mix";
AppContext::AppContext(QQmlApplicationEngine* _engine) AppContext::AppContext(QQmlApplicationEngine* _engine)
{ {
m_applicationEngine = _engine; m_applicationEngine = _engine;
//m_webThree = std::unique_ptr<dev::WebThreeDirect>(new WebThreeDirect(std::string("Mix/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/Mix", false, {"eth", "shh"}));
m_codeModel.reset(new CodeModel(this)); m_codeModel.reset(new CodeModel(this));
m_clientModel.reset(new ClientModel(this)); m_clientModel.reset(new ClientModel(this));
m_fileIo.reset(new FileIo()); m_fileIo.reset(new FileIo());
/*
m_applicationEngine->rootContext()->setContextProperty("appContext", this);
qmlRegisterType<FileIo>("org.ethereum.qml", 1, 0, "FileIo");
qmlRegisterSingletonType(QUrl("qrc:/qml/ProjectModel.qml"), "org.ethereum.qml.ProjectModel", 1, 0, "ProjectModel");
qmlRegisterType<QEther>("org.ethereum.qml.QEther", 1, 0, "QEther");
qmlRegisterType<QBigInt>("org.ethereum.qml.QBigInt", 1, 0, "QBigInt");
m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get());
m_applicationEngine->rootContext()->setContextProperty("fileIo", m_fileIo.get());
*/
} }
AppContext::~AppContext() AppContext::~AppContext()
@ -82,6 +72,7 @@ void AppContext::load()
} }
m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel); m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel);
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
m_applicationEngine->load(QUrl("qrc:/qml/main.qml")); m_applicationEngine->load(QUrl("qrc:/qml/main.qml"));
appLoaded(); appLoaded();
} }

9
mix/AppContext.h

@ -32,14 +32,6 @@
#include <QObject> #include <QObject>
class QQmlApplicationEngine; class QQmlApplicationEngine;
namespace dev
{
class WebThreeDirect;
namespace solidity
{
class CompilerStack;
}
}
namespace dev namespace dev
{ {
@ -77,7 +69,6 @@ signals:
private: private:
QQmlApplicationEngine* m_applicationEngine; //owned by app QQmlApplicationEngine* m_applicationEngine; //owned by app
std::unique_ptr<dev::WebThreeDirect> m_webThree;
std::unique_ptr<CodeModel> m_codeModel; std::unique_ptr<CodeModel> m_codeModel;
std::unique_ptr<ClientModel> m_clientModel; std::unique_ptr<ClientModel> m_clientModel;
std::unique_ptr<FileIo> m_fileIo; std::unique_ptr<FileIo> m_fileIo;

5
mix/ClientModel.cpp

@ -68,10 +68,9 @@ ClientModel::~ClientModel()
{ {
} }
void ClientModel::apiRequest(QString const& _message) QString ClientModel::apiCall(QString const& _message)
{ {
(void)_message; return m_qWebThree->callMethod(_message);
// m_qWebThree->postMessage(_message);
} }
QString ClientModel::contractAddress() const QString ClientModel::contractAddress() const

7
mix/ClientModel.h

@ -76,11 +76,12 @@ public:
Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged) Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged)
/// @returns address of the last executed contract /// @returns address of the last executed contract
Q_PROPERTY(QString contractAddress READ contractAddress NOTIFY contractAddressChanged) Q_PROPERTY(QString contractAddress READ contractAddress NOTIFY contractAddressChanged)
public slots:
/// ethereum.js RPC request entry point /// ethereum.js RPC request entry point
/// @param _message RPC request in Json format /// @param _message RPC request in Json format
void apiRequest(QString const& _message); /// @returns RPC response in Json format
Q_INVOKABLE QString apiCall(QString const& _message);
public slots:
/// Run the contract constructor and show debugger window. /// Run the contract constructor and show debugger window.
void debugDeployment(); void debugDeployment();
/// Setup state, run transaction sequence, show debugger for the last transaction /// Setup state, run transaction sequence, show debugger for the last transaction

170
mix/HttpServer.cpp

@ -0,0 +1,170 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file HttpServer.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#include <memory>
#include <QTcpSocket>
#include "HttpServer.h"
using namespace dev::mix;
HttpServer::HttpServer()
: m_port(0) , m_listen(false) , m_accept(true) , m_componentCompleted(true)
{
}
HttpServer::~HttpServer()
{
setListen(false);
}
void HttpServer::classBegin()
{
m_componentCompleted = false;
}
void HttpServer::componentComplete()
{
init();
m_componentCompleted = true;
}
QUrl HttpServer::url() const
{
QUrl url;
url.setPort(m_port);
url.setHost("localhost");
url.setScheme("http");
return url;
}
void HttpServer::setPort(int _port)
{
if (_port == m_port)
return;
m_port = _port;
emit portChanged(_port);
emit urlChanged(url());
if (m_componentCompleted && this->isListening())
updateListening();
}
QString HttpServer::errorString() const
{
return this->errorString();
}
void HttpServer::setListen(bool _listen)
{
if (_listen == m_listen)
return;
m_listen = _listen;
emit listenChanged(_listen);
if (m_componentCompleted)
updateListening();
}
void HttpServer::setAccept(bool _accept)
{
if (_accept == m_accept)
return;
m_accept = _accept;
emit acceptChanged(_accept);
}
void HttpServer::init()
{
updateListening();
}
void HttpServer::updateListening()
{
if (this->isListening())
this->close();
if (!m_listen || QTcpServer::listen(QHostAddress::LocalHost, m_port))
return;
}
void HttpServer::incomingConnection(qintptr _socket)
{
if (!m_accept)
return;
QTcpSocket* s = new QTcpSocket(this);
connect(s, SIGNAL(readyRead()), this, SLOT(readClient()));
connect(s, SIGNAL(disconnected()), this, SLOT(discardClient()));
s->setSocketDescriptor(_socket);
}
void HttpServer::readClient()
{
if (!m_accept)
return;
QTcpSocket* socket = (QTcpSocket*)sender();
try
{
if (socket->canReadLine())
{
QString hdr = QString(socket->readLine());
if (hdr.startsWith("POST"))
{
QString l;
do
l = socket->readLine();
while (!(l.isEmpty() || l == "\r" || l == "\r\n"));
QString content = socket->readAll();
QUrl url;
std::unique_ptr<HttpRequest> request(new HttpRequest(this, url, content));
clientConnected(request.get());
QTextStream os(socket);
os.setAutoDetectUnicode(true);
///@todo: allow setting response content-type, charset, etc
os << "HTTP/1.0 200 Ok\r\n"
"Content-Type: text/plain; charset=\"utf-8\"\r\n"
"\r\n";
os << request->m_response;
}
}
}
catch(...)
{
delete socket;
throw;
}
socket->close();
if (socket->state() == QTcpSocket::UnconnectedState)
delete socket;
}
void HttpServer::discardClient()
{
QTcpSocket* socket = (QTcpSocket*)sender();
socket->deleteLater();
}

123
mix/HttpServer.h

@ -0,0 +1,123 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file HttpServer.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <QObject>
#include <QTcpServer>
#include <QUrl>
#include <QQmlParserStatus>
namespace dev
{
namespace mix
{
/// Simple http server for serving jsonrpc requests
class HttpRequest : public QObject
{
Q_OBJECT
/// Request url
Q_PROPERTY(QUrl url MEMBER m_url CONSTANT)
/// Request body contents
Q_PROPERTY(QString content MEMBER m_content CONSTANT)
private:
HttpRequest(QObject* _parent, QUrl const& _url, QString const& _content):
QObject(_parent), m_url(_url), m_content(_content)
{
}
public:
/// Set response for a request
/// @param _response Response body. If no response is set, server returns status 200 with empty body
Q_INVOKABLE void setResponse(QString const& _response) { m_response = _response; }
private:
QUrl m_url;
QString m_content;
QString m_response;
friend class HttpServer;
};
class HttpServer : public QTcpServer, public QQmlParserStatus
{
Q_OBJECT
Q_DISABLE_COPY(HttpServer)
Q_INTERFACES(QQmlParserStatus)
/// Server url
Q_PROPERTY(QUrl url READ url NOTIFY urlChanged)
/// Server port
Q_PROPERTY(int port READ port WRITE setPort NOTIFY portChanged)
/// Listen for connections
Q_PROPERTY(bool listen READ listen WRITE setListen NOTIFY listenChanged)
/// Accept new connections
Q_PROPERTY(bool accept READ accept WRITE setAccept NOTIFY acceptChanged)
/// Error string if any
Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged)
public:
explicit HttpServer();
virtual ~HttpServer();
QUrl url() const;
int port() const { return m_port; }
void setPort(int _port);
bool listen() const { return m_listen; }
void setListen(bool _listen);
bool accept() const { return m_accept; }
void setAccept(bool _accept);
QString errorString() const;
protected:
virtual void classBegin() override;
virtual void componentComplete() override;
virtual void incomingConnection(qintptr _socket) override;
signals:
void clientConnected(HttpRequest* _request);
void errorStringChanged(QString const& _errorString);
void urlChanged(QUrl const& _url);
void portChanged(int _port);
void listenChanged(bool _listen);
void acceptChanged(bool _accept);
private:
void init();
void updateListening();
void newConnection();
void serverError();
private slots:
void readClient();
void discardClient();
private:
int m_port;
bool m_listen;
bool m_accept;
bool m_componentCompleted;
};
}
}

31
mix/qml/WebPreview.qml

@ -4,7 +4,8 @@ import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0 import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import QtWebEngine 1.0 import QtWebEngine 1.0
import Qt.WebSockets 1.0 import QtWebEngine.experimental 1.0
import HttpServer 1.0
Item { Item {
id: webPreview id: webPreview
@ -53,7 +54,7 @@ Item {
onAppLoaded: { onAppLoaded: {
//We need to load the container using file scheme so that web security would allow loading local files in iframe //We need to load the container using file scheme so that web security would allow loading local files in iframe
var containerPage = fileIo.readFile("qrc:///qml/html/WebContainer.html"); var containerPage = fileIo.readFile("qrc:///qml/html/WebContainer.html");
webView.loadHtml(containerPage, "file:///") webView.loadHtml(containerPage, "file:///WebContainer.html")
} }
} }
@ -104,20 +105,16 @@ Item {
id: pageListModel id: pageListModel
} }
WebSocketServer { HttpServer {
id: socketServer id: httpServer
listen: true listen: true
name: "mix" accept: true
onClientConnected: port: 8892
{ onClientConnected: {
webSocket.onTextMessageReceived.connect(function(message) { console.log(_request.content);
console.log("rpc_request: " + message); var response = clientModel.apiCall(_request.content);
clientModel.apiRequest(message); console.log(response);
}); _request.setResponse(response);
clientModel.onApiResponse.connect(function(message) {
console.log("rpc_response: " + message);
webSocket.sendTextMessage(message);
});
} }
} }
@ -152,13 +149,15 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
id: webView id: webView
experimental.settings.localContentCanAccessFileUrls: true
experimental.settings.localContentCanAccessRemoteUrls: true
onJavaScriptConsoleMessage: { onJavaScriptConsoleMessage: {
console.log(sourceID + ":" + lineNumber + ":" + message); console.log(sourceID + ":" + lineNumber + ":" + message);
} }
onLoadingChanged: { onLoadingChanged: {
if (!loading) { if (!loading) {
initialized = true; initialized = true;
webView.runJavaScript("init(\"" + socketServer.url + "\")"); webView.runJavaScript("init(\"" + httpServer.url + "\")");
if (pendingPageUrl) if (pendingPageUrl)
setPreviewUrl(pendingPageUrl); setPreviewUrl(pendingPageUrl);
} }

2
mix/qml/html/WebContainer.html

@ -26,7 +26,7 @@ updateContract = function(address, contractFace) {
init = function(url) { init = function(url) {
web3 = require('web3'); web3 = require('web3');
web3.setProvider(new web3.providers.WebSocketProvider(url)); web3.setProvider(new web3.providers.HttpSyncProvider(url));
window.web3 = web3; window.web3 = web3;
}; };

Loading…
Cancel
Save