Gav Wood
10 years ago
22 changed files with 613 additions and 200 deletions
@ -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(); |
|||
} |
@ -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; |
|||
}; |
|||
|
|||
} |
|||
} |
Loading…
Reference in new issue