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();
}
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)
{
QJsonObject f = QJsonDocument::fromJson(_json.toUtf8()).object();
emit processData(formatInput(f), ""); // it's synchronous
emit processData(_json, ""); // it's synchronous
return m_response;
}

13
mix/AppContext.cpp

@ -33,7 +33,7 @@
#include "Exceptions.h"
#include "AppContext.h"
#include "QEther.h"
#include <libwebthree/WebThree.h>
#include "HttpServer.h"
using namespace dev;
using namespace dev::eth;
@ -44,19 +44,9 @@ const QString c_projectFileName = "project.mix";
AppContext::AppContext(QQmlApplicationEngine* _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_clientModel.reset(new ClientModel(this));
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()
@ -82,6 +72,7 @@ void AppContext::load()
}
m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel);
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
m_applicationEngine->load(QUrl("qrc:/qml/main.qml"));
appLoaded();
}

9
mix/AppContext.h

@ -32,14 +32,6 @@
#include <QObject>
class QQmlApplicationEngine;
namespace dev
{
class WebThreeDirect;
namespace solidity
{
class CompilerStack;
}
}
namespace dev
{
@ -77,7 +69,6 @@ signals:
private:
QQmlApplicationEngine* m_applicationEngine; //owned by app
std::unique_ptr<dev::WebThreeDirect> m_webThree;
std::unique_ptr<CodeModel> m_codeModel;
std::unique_ptr<ClientModel> m_clientModel;
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;
// m_qWebThree->postMessage(_message);
return m_qWebThree->callMethod(_message);
}
QString ClientModel::contractAddress() const

7
mix/ClientModel.h

@ -76,11 +76,12 @@ public:
Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged)
/// @returns address of the last executed contract
Q_PROPERTY(QString contractAddress READ contractAddress NOTIFY contractAddressChanged)
public slots:
/// ethereum.js RPC request entry point
/// @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.
void debugDeployment();
/// 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.Styles 1.1
import QtWebEngine 1.0
import Qt.WebSockets 1.0
import QtWebEngine.experimental 1.0
import HttpServer 1.0
Item {
id: webPreview
@ -53,7 +54,7 @@ Item {
onAppLoaded: {
//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");
webView.loadHtml(containerPage, "file:///")
webView.loadHtml(containerPage, "file:///WebContainer.html")
}
}
@ -104,20 +105,16 @@ Item {
id: pageListModel
}
WebSocketServer {
id: socketServer
HttpServer {
id: httpServer
listen: true
name: "mix"
onClientConnected:
{
webSocket.onTextMessageReceived.connect(function(message) {
console.log("rpc_request: " + message);
clientModel.apiRequest(message);
});
clientModel.onApiResponse.connect(function(message) {
console.log("rpc_response: " + message);
webSocket.sendTextMessage(message);
});
accept: true
port: 8892
onClientConnected: {
console.log(_request.content);
var response = clientModel.apiCall(_request.content);
console.log(response);
_request.setResponse(response);
}
}
@ -152,13 +149,15 @@ Item {
Layout.fillWidth: true
Layout.fillHeight: true
id: webView
experimental.settings.localContentCanAccessFileUrls: true
experimental.settings.localContentCanAccessRemoteUrls: true
onJavaScriptConsoleMessage: {
console.log(sourceID + ":" + lineNumber + ":" + message);
}
onLoadingChanged: {
if (!loading) {
initialized = true;
webView.runJavaScript("init(\"" + socketServer.url + "\")");
webView.runJavaScript("init(\"" + httpServer.url + "\")");
if (pendingPageUrl)
setPreviewUrl(pendingPageUrl);
}

2
mix/qml/html/WebContainer.html

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

Loading…
Cancel
Save