added more tests
move httprequest and reply to server added request parsing logic
This commit is contained in:
parent
70a929f760
commit
ea9d8d6d90
@ -39,8 +39,7 @@ SOURCES += plugin/device.cpp \
|
||||
types/statedescriptor.cpp \
|
||||
loggingcategories.cpp \
|
||||
guhsettings.cpp \
|
||||
network/httpreply.cpp \
|
||||
network/httprequest.cpp
|
||||
|
||||
|
||||
HEADERS += plugin/device.h \
|
||||
plugin/deviceclass.h \
|
||||
@ -74,6 +73,4 @@ HEADERS += plugin/device.h \
|
||||
typeutils.h \
|
||||
loggingcategories.h \
|
||||
guhsettings.h \
|
||||
network/httpreply.h \
|
||||
network/httprequest.h
|
||||
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
|
||||
\note RFC 7231 HTTP/1.1 Semantics and Content -> \l{http://tools.ietf.org/html/rfc7231}{http://tools.ietf.org/html/rfc7231}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/*! \enum HttpReply::HttpStatusCode
|
||||
@ -105,6 +104,7 @@
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QPair>
|
||||
#include <QDebug>
|
||||
|
||||
/*! Construct a HttpReply with the given \a statusCode. */
|
||||
HttpReply::HttpReply(QObject *parent) :
|
||||
@ -114,7 +114,10 @@ HttpReply::HttpReply(QObject *parent) :
|
||||
m_payload(QByteArray()),
|
||||
m_timedOut(false)
|
||||
{
|
||||
connect(&m_timer, &QTimer::timeout, this, &HttpReply::timedOut);
|
||||
m_timer = new QTimer(this);
|
||||
connect(m_timer, &QTimer::timeout, this, &HttpReply::timedOut);
|
||||
|
||||
m_reasonPhrase = getHttpReasonPhrase(m_statusCode);
|
||||
|
||||
// set known headers
|
||||
setHeader(HttpHeaderType::ServerHeader, "guh/" + QByteArray(GUH_VERSION_STRING));
|
||||
@ -130,7 +133,10 @@ HttpReply::HttpReply(const HttpReply::HttpStatusCode &statusCode, const HttpRepl
|
||||
m_payload(QByteArray()),
|
||||
m_timedOut(false)
|
||||
{
|
||||
connect(&m_timer, &QTimer::timeout, this, &HttpReply::timedOut);
|
||||
m_timer = new QTimer(this);
|
||||
connect(m_timer, &QTimer::timeout, this, &HttpReply::timeout);
|
||||
|
||||
m_reasonPhrase = getHttpReasonPhrase(m_statusCode);
|
||||
|
||||
// set known headers
|
||||
setHeader(HttpHeaderType::ServerHeader, "guh/" + QByteArray(GUH_VERSION_STRING));
|
||||
@ -152,6 +158,11 @@ HttpReply::HttpStatusCode HttpReply::httpStatusCode() const
|
||||
return m_statusCode;
|
||||
}
|
||||
|
||||
QByteArray HttpReply::httpReasonPhrase() const
|
||||
{
|
||||
return m_reasonPhrase;
|
||||
}
|
||||
|
||||
/*! Returns the type of this \l{HttpReply}.
|
||||
* \sa Type
|
||||
*/
|
||||
@ -332,11 +343,12 @@ QByteArray HttpReply::getHeaderType(const HttpReply::HttpHeaderType &headerType)
|
||||
|
||||
void HttpReply::startWait()
|
||||
{
|
||||
m_timer.start(5000);
|
||||
m_timer->start(5000);
|
||||
}
|
||||
|
||||
void HttpReply::timeout()
|
||||
{
|
||||
qDebug() << "Http reply timeout";
|
||||
m_timedOut = true;
|
||||
emit finished();
|
||||
}
|
||||
@ -77,6 +77,8 @@ public:
|
||||
void setHttpStatusCode(const HttpStatusCode &statusCode);
|
||||
HttpStatusCode httpStatusCode() const;
|
||||
|
||||
QByteArray httpReasonPhrase() const;
|
||||
|
||||
Type type() const;
|
||||
|
||||
void setClientId(const QUuid &clientId);
|
||||
@ -101,6 +103,7 @@ public:
|
||||
|
||||
private:
|
||||
HttpStatusCode m_statusCode;
|
||||
QByteArray m_reasonPhrase;
|
||||
Type m_type;
|
||||
QUuid m_clientId;
|
||||
|
||||
@ -110,20 +113,21 @@ private:
|
||||
|
||||
QHash<QByteArray, QByteArray> m_rawHeaderList;
|
||||
|
||||
QTimer m_timer;
|
||||
QTimer *m_timer;
|
||||
bool m_timedOut;
|
||||
|
||||
QByteArray getHttpReasonPhrase(const HttpStatusCode &statusCode);
|
||||
QByteArray getHeaderType(const HttpHeaderType &headerType);
|
||||
|
||||
private slots:
|
||||
void timeout();
|
||||
|
||||
public slots:
|
||||
void startWait();
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
private slots:
|
||||
void timeout();
|
||||
|
||||
};
|
||||
|
||||
@ -24,61 +24,19 @@
|
||||
#include <QUrlQuery>
|
||||
|
||||
|
||||
HttpRequest::HttpRequest() :
|
||||
m_rawData(QByteArray()),
|
||||
m_valid(false),
|
||||
m_isComplete(false)
|
||||
{
|
||||
}
|
||||
|
||||
HttpRequest::HttpRequest(QByteArray rawData) :
|
||||
m_rawData(rawData),
|
||||
m_valid(false)
|
||||
m_valid(false),
|
||||
m_isComplete(false)
|
||||
{
|
||||
// Parese the HTTP request. The request is invalid, until the end of the parse process.
|
||||
if (m_rawData.isEmpty())
|
||||
return;
|
||||
|
||||
// split the data into header and payload
|
||||
int headerEndIndex = m_rawData.indexOf("\r\n\r\n");
|
||||
if (headerEndIndex < 0) {
|
||||
qCWarning(dcWebServer) << "Could not parse end of HTTP header (empty line between header and body):" << rawData;
|
||||
return;
|
||||
}
|
||||
m_rawHeader = m_rawData.left(headerEndIndex);
|
||||
m_payload = m_rawData.right(m_rawData.length() - headerEndIndex).simplified();
|
||||
|
||||
// parse status line
|
||||
QStringList headerLines = QString(m_rawHeader).split(QRegExp("\r\n"));
|
||||
QString statusLine = headerLines.takeFirst();
|
||||
QStringList statusLineTokens = statusLine.split(QRegExp("[ \r\n][ \r\n]*"));
|
||||
if (statusLineTokens.count() != 3) {
|
||||
qCWarning(dcWebServer) << "Could not parse HTTP status line:" << statusLine;
|
||||
return;
|
||||
}
|
||||
|
||||
// verify http version
|
||||
m_httpVersion = statusLineTokens.at(2).toUtf8().simplified();
|
||||
if (!m_httpVersion.contains("HTTP")) {
|
||||
qCWarning(dcWebServer) << "Unknown HTTP version:" << m_httpVersion;
|
||||
return;
|
||||
}
|
||||
m_methodString = statusLineTokens.at(0).simplified();
|
||||
m_method = getRequestMethodType(m_methodString);
|
||||
|
||||
m_url = QUrl("http://example.com" + statusLineTokens.at(1).simplified());
|
||||
|
||||
if (m_url.hasQuery())
|
||||
m_urlQuery = QUrlQuery(m_url.query());
|
||||
|
||||
// verify headers
|
||||
foreach (const QString &line, headerLines) {
|
||||
if (!line.contains(":")) {
|
||||
qCWarning(dcWebServer) << "Invalid HTTP header:" << line;
|
||||
return;
|
||||
}
|
||||
int index = line.indexOf(":");
|
||||
QByteArray key = line.left(index).toUtf8().simplified();
|
||||
QByteArray value = line.right(line.count() - index - 1).toUtf8().simplified();
|
||||
m_rawHeaderList.insert(key, value);
|
||||
}
|
||||
|
||||
// TODO: check content size
|
||||
|
||||
m_valid = true;
|
||||
validate();
|
||||
}
|
||||
|
||||
QByteArray HttpRequest::rawHeader() const
|
||||
@ -126,11 +84,109 @@ bool HttpRequest::isValid() const
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
bool HttpRequest::isComplete() const
|
||||
{
|
||||
return m_isComplete;
|
||||
}
|
||||
|
||||
bool HttpRequest::hasPayload() const
|
||||
{
|
||||
return !m_payload.isEmpty();
|
||||
}
|
||||
|
||||
void HttpRequest::appendData(const QByteArray &data)
|
||||
{
|
||||
m_rawData.append(data);
|
||||
validate();
|
||||
}
|
||||
|
||||
void HttpRequest::validate()
|
||||
{
|
||||
m_isComplete = true; m_valid = false;
|
||||
|
||||
// Parese the HTTP request. The request is invalid, until the end of the parse process.
|
||||
if (m_rawData.isEmpty())
|
||||
return;
|
||||
|
||||
// split the data into header and payload
|
||||
int headerEndIndex = m_rawData.indexOf("\r\n\r\n");
|
||||
if (headerEndIndex < 0) {
|
||||
qCWarning(dcWebServer) << "Could not parse end of HTTP header (empty line between header and body):" << m_rawData;
|
||||
return;
|
||||
}
|
||||
m_rawHeader = m_rawData.left(headerEndIndex);
|
||||
m_payload = m_rawData.right(m_rawData.length() - headerEndIndex).simplified();
|
||||
|
||||
// parse status line
|
||||
QStringList headerLines = QString(m_rawHeader).split(QRegExp("\r\n"));
|
||||
QString statusLine = headerLines.takeFirst();
|
||||
QStringList statusLineTokens = statusLine.split(QRegExp("[ \r\n][ \r\n]*"));
|
||||
if (statusLineTokens.count() != 3) {
|
||||
qCWarning(dcWebServer) << "Could not parse HTTP status line:" << statusLine;
|
||||
return;
|
||||
}
|
||||
|
||||
// verify http version
|
||||
m_httpVersion = statusLineTokens.at(2).toUtf8().simplified();
|
||||
if (!m_httpVersion.contains("HTTP")) {
|
||||
qCWarning(dcWebServer) << "Unknown HTTP version:" << m_httpVersion;
|
||||
return;
|
||||
}
|
||||
m_methodString = statusLineTokens.at(0).simplified();
|
||||
m_method = getRequestMethodType(m_methodString);
|
||||
|
||||
m_url = QUrl("http://example.com" + statusLineTokens.at(1).simplified());
|
||||
|
||||
if (m_url.hasQuery())
|
||||
m_urlQuery = QUrlQuery(m_url.query());
|
||||
|
||||
// verify header formating
|
||||
foreach (const QString &line, headerLines) {
|
||||
if (!line.contains(":")) {
|
||||
qCWarning(dcWebServer) << "Invalid HTTP header:" << line;
|
||||
return;
|
||||
}
|
||||
int index = line.indexOf(":");
|
||||
QByteArray key = line.left(index).toUtf8().simplified();
|
||||
QByteArray value = line.right(line.count() - index - 1).toUtf8().simplified();
|
||||
m_rawHeaderList.insert(key, value);
|
||||
}
|
||||
|
||||
// check User-Agent
|
||||
if (!m_rawHeaderList.contains("User-Agent")) {
|
||||
qWarning() << "User-Agent header is missing";
|
||||
return;
|
||||
}
|
||||
|
||||
// verify content length with actual payload
|
||||
if (m_rawHeaderList.contains("Content-Length")) {
|
||||
bool ok = false;
|
||||
int contentLength = m_rawHeaderList.value("Content-Length").toInt(&ok);
|
||||
if (!ok) {
|
||||
qCWarning(dcWebServer) << "Could not parse Content-Length.";
|
||||
return;
|
||||
}
|
||||
// check if we have all data
|
||||
if (m_payload.size() < contentLength) {
|
||||
qCDebug(dcWebServer) << "Request incomplete:";
|
||||
qCDebug(dcWebServer) << " -> Content-Length:" << contentLength;
|
||||
qCDebug(dcWebServer) << " -> Payload size :" << payload().size();
|
||||
m_isComplete = false;
|
||||
return;
|
||||
}
|
||||
// check if the content lenght bigger than header Content-Length
|
||||
if (m_payload.size() > contentLength) {
|
||||
qCWarning(dcWebServer) << "Payload size greater than header Content-Length:";
|
||||
qCWarning(dcWebServer) << " -> Content-Length:" << contentLength;
|
||||
qCWarning(dcWebServer) << " -> Payload size :" << payload().size();
|
||||
m_isComplete = true;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
m_valid = true;
|
||||
}
|
||||
|
||||
HttpRequest::RequestMethod HttpRequest::getRequestMethodType(const QString &methodString)
|
||||
{
|
||||
if (methodString == "GET") {
|
||||
@ -149,7 +205,7 @@ HttpRequest::RequestMethod HttpRequest::getRequestMethodType(const QString &meth
|
||||
QDebug operator<<(QDebug debug, const HttpRequest &httpRequest)
|
||||
{
|
||||
debug << "\n===================================" << "\n";
|
||||
debug << " http version: " << httpRequest.httpVersion() << "\n";
|
||||
debug << " HTTP version: " << httpRequest.httpVersion() << "\n";
|
||||
debug << " method: " << httpRequest.methodString() << "\n";
|
||||
debug << " URL path: " << httpRequest.url().path() << "\n";
|
||||
debug << " URL query: " << httpRequest.urlQuery().query() << "\n";
|
||||
@ -37,7 +37,8 @@ public:
|
||||
Unhandled
|
||||
};
|
||||
|
||||
explicit HttpRequest(QByteArray rawData);
|
||||
HttpRequest();
|
||||
HttpRequest(QByteArray rawData);
|
||||
|
||||
QByteArray rawHeader() const;
|
||||
QHash<QByteArray, QByteArray> rawHeaderList() const;
|
||||
@ -52,8 +53,11 @@ public:
|
||||
QByteArray payload() const;
|
||||
|
||||
bool isValid() const;
|
||||
bool isComplete() const;
|
||||
bool hasPayload() const;
|
||||
|
||||
void appendData(const QByteArray &data);
|
||||
|
||||
private:
|
||||
QByteArray m_rawData;
|
||||
QByteArray m_rawHeader;
|
||||
@ -69,7 +73,9 @@ private:
|
||||
QByteArray m_payload;
|
||||
|
||||
bool m_valid;
|
||||
bool m_isComplete;
|
||||
|
||||
void validate();
|
||||
RequestMethod getRequestMethodType(const QString &methodString);
|
||||
};
|
||||
|
||||
@ -632,7 +632,6 @@ void DeviceHandler::pairingFinished(const PairingTransactionId &pairingTransacti
|
||||
reply->finished();
|
||||
return;
|
||||
}
|
||||
|
||||
m_asynDeviceAdditions.insert(deviceId, reply);
|
||||
}
|
||||
|
||||
|
||||
@ -702,7 +702,11 @@ QVariantList JsonTypes::packSupportedDevices(const VendorId &vendorId)
|
||||
|
||||
QVariantList JsonTypes::packConfiguredDevices()
|
||||
{
|
||||
return packRules(GuhCore::instance()->rules());
|
||||
QVariantList configuredDeviceList;
|
||||
foreach (Device *device, GuhCore::instance()->configuredDevices()) {
|
||||
configuredDeviceList.append(packDevice(device));
|
||||
}
|
||||
return configuredDeviceList;
|
||||
}
|
||||
|
||||
QVariantList JsonTypes::packDeviceStates(Device *device)
|
||||
@ -736,7 +740,7 @@ QVariantList JsonTypes::packRuleDescriptions()
|
||||
return rulesList;
|
||||
}
|
||||
|
||||
QVariantList JsonTypes::packRuleDescriptions(const QList<Rule> rules)
|
||||
QVariantList JsonTypes::packRuleDescriptions(const QList<Rule> &rules)
|
||||
{
|
||||
QVariantList rulesList;
|
||||
foreach (const Rule &rule, rules) {
|
||||
|
||||
@ -169,7 +169,7 @@ public:
|
||||
static QVariantList packDeviceDescriptors(const QList<DeviceDescriptor> deviceDescriptors);
|
||||
|
||||
static QVariantList packRuleDescriptions();
|
||||
static QVariantList packRuleDescriptions(const QList<Rule> rules);
|
||||
static QVariantList packRuleDescriptions(const QList<Rule> &rules);
|
||||
|
||||
static QVariantList packActionTypes(const DeviceClass &deviceClass);
|
||||
static QVariantList packStateTypes(const DeviceClass &deviceClass);
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "deviceclassesresource.h"
|
||||
#include "network/httprequest.h"
|
||||
#include "httprequest.h"
|
||||
#include "guhcore.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
@ -135,19 +135,17 @@ HttpReply *DeviceClassesResource::proccessGetRequest(const HttpRequest &request,
|
||||
|
||||
// GET /api/v1/deviceclasses/{deviceClassId}/discover?name=paramName&value=paramValue
|
||||
if (urlTokens.count() == 5 && urlTokens.at(4) == "discover") {
|
||||
ParamList params;
|
||||
if (request.urlQuery().hasQueryItem("params")) {
|
||||
QString paramMapString = request.urlQuery().queryItemValue("params");
|
||||
|
||||
// FIXME: find a better way to get discovery params
|
||||
|
||||
ParamList paramList;
|
||||
if (request.url().hasQuery()) {
|
||||
if (request.urlQuery().hasQueryItem("name") && request.urlQuery().hasQueryItem("name")) {
|
||||
paramList.append(Param(request.urlQuery().queryItemValue("name"), QVariant(request.urlQuery().queryItemValue("value"))));
|
||||
} else {
|
||||
qCWarning(dcRest) << "Invalid discovery params in" << request.urlQuery().query();
|
||||
QPair<bool, QVariant> verification = verifyPayload(paramMapString.toUtf8());
|
||||
if (!verification.first)
|
||||
return createErrorReply(HttpReply::BadRequest);
|
||||
}
|
||||
|
||||
params = JsonTypes::unpackParams(verification.second.toList());
|
||||
}
|
||||
return getDiscoverdDevices(paramList);
|
||||
return getDiscoverdDevices(params);
|
||||
}
|
||||
|
||||
return createErrorReply(HttpReply::BadRequest);
|
||||
@ -230,6 +228,7 @@ HttpReply *DeviceClassesResource::getEventType(const EventTypeId &eventTypeId)
|
||||
HttpReply *DeviceClassesResource::getDiscoverdDevices(const ParamList &discoveryParams)
|
||||
{
|
||||
qCDebug(dcRest) << "Discover devices for DeviceClass" << m_deviceClass.id();
|
||||
qCDebug(dcRest) << discoveryParams;
|
||||
|
||||
DeviceManager::DeviceError status = GuhCore::instance()->discoverDevices(m_deviceClass.id(), discoveryParams);
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
#include "jsontypes.h"
|
||||
#include "restresource.h"
|
||||
#include "network/httpreply.h"
|
||||
#include "httpreply.h"
|
||||
|
||||
class HttpRequest;
|
||||
|
||||
|
||||
@ -19,8 +19,8 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "devicesresource.h"
|
||||
#include "network/httpreply.h"
|
||||
#include "network/httprequest.h"
|
||||
#include "httpreply.h"
|
||||
#include "httprequest.h"
|
||||
#include "jsontypes.h"
|
||||
#include "guhcore.h"
|
||||
|
||||
@ -45,6 +45,7 @@ HttpReply *DevicesResource::proccessRequest(const HttpRequest &request, const QS
|
||||
{
|
||||
m_device = 0;
|
||||
|
||||
|
||||
// get the main resource
|
||||
if (urlTokens.count() >= 4) {
|
||||
DeviceId deviceId = DeviceId(urlTokens.at(3));
|
||||
@ -147,6 +148,7 @@ HttpReply *DevicesResource::proccessPutRequest(const HttpRequest &request, const
|
||||
|
||||
HttpReply *DevicesResource::proccessPostRequest(const HttpRequest &request, const QStringList &urlTokens)
|
||||
{
|
||||
|
||||
// POST /api/v1/devices
|
||||
if (urlTokens.count() == 3)
|
||||
return addConfiguredDevice(request.payload());
|
||||
@ -162,6 +164,19 @@ HttpReply *DevicesResource::proccessPostRequest(const HttpRequest &request, cons
|
||||
qCWarning(dcRest) << "Could not parse ActionTypeId:" << urlTokens.at(5);
|
||||
return createErrorReply(HttpReply::BadRequest);
|
||||
}
|
||||
bool found = false;
|
||||
DeviceClass deviceClass = GuhCore::instance()->findDeviceClass(m_device->deviceClassId());
|
||||
foreach (const ActionType actionType, deviceClass.actionTypes()) {
|
||||
if (actionType.id() == actionTypeId) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
qCWarning(dcRest) << "Could not find ActionTypeId:" << actionTypeId.toString();
|
||||
return createErrorReply(HttpReply::NotFound);
|
||||
}
|
||||
|
||||
return executeAction(m_device, actionTypeId, request.payload());
|
||||
}
|
||||
|
||||
@ -217,8 +232,6 @@ HttpReply *DevicesResource::removeDevice(Device *device) const
|
||||
|
||||
HttpReply *DevicesResource::executeAction(Device *device, const ActionTypeId &actionTypeId, const QByteArray &payload) const
|
||||
{
|
||||
qCDebug(dcRest) << "Execute action" << actionTypeId.toString();
|
||||
|
||||
QPair<bool, QVariant> verification = RestResource::verifyPayload(payload);
|
||||
if (!verification.first)
|
||||
return createErrorReply(HttpReply::BadRequest);
|
||||
@ -230,6 +243,8 @@ HttpReply *DevicesResource::executeAction(Device *device, const ActionTypeId &ac
|
||||
|
||||
ParamList actionParams = JsonTypes::unpackParams(message.value("params").toList());
|
||||
|
||||
qCDebug(dcRest) << "Execute action with" << actionParams;
|
||||
|
||||
Action action(actionTypeId, device->id());
|
||||
action.setParams(actionParams);
|
||||
|
||||
@ -252,33 +267,39 @@ HttpReply *DevicesResource::addConfiguredDevice(const QByteArray &payload) const
|
||||
if (!verification.first)
|
||||
return createErrorReply(HttpReply::BadRequest);
|
||||
|
||||
qCDebug(dcRest) << "Add device";
|
||||
|
||||
QVariantMap params = verification.second.toMap();
|
||||
|
||||
DeviceClassId deviceClass(params.value("deviceClassId").toString());
|
||||
DeviceClassId deviceClassId(params.value("deviceClassId").toString());
|
||||
if (deviceClassId.isNull())
|
||||
return createErrorReply(HttpReply::BadRequest);
|
||||
|
||||
DeviceId newDeviceId = DeviceId::createDeviceId();
|
||||
ParamList deviceParams = JsonTypes::unpackParams(params.value("deviceParams").toList());
|
||||
DeviceDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString());
|
||||
DeviceId newDeviceId = DeviceId::createDeviceId();
|
||||
|
||||
DeviceManager::DeviceError status;
|
||||
if (deviceDescriptorId.isNull()) {
|
||||
qCDebug(dcRest) << "Adding device with params" << deviceParams;
|
||||
status = GuhCore::instance()->addConfiguredDevice(deviceClass, deviceParams, newDeviceId);
|
||||
qCDebug(dcRest) << "Adding device with " << deviceParams;
|
||||
status = GuhCore::instance()->addConfiguredDevice(deviceClassId, deviceParams, newDeviceId);
|
||||
} else {
|
||||
qCDebug(dcRest) << "Adding discovered device";
|
||||
status = GuhCore::instance()->addConfiguredDevice(deviceClass, deviceDescriptorId, newDeviceId);
|
||||
status = GuhCore::instance()->addConfiguredDevice(deviceClassId, deviceDescriptorId, newDeviceId);
|
||||
}
|
||||
if (status == DeviceManager::DeviceErrorAsync) {
|
||||
HttpReply *reply = createAsyncReply();
|
||||
m_asynDeviceAdditions.insert(newDeviceId, reply);
|
||||
qCDebug(dcRest) << "Device setup async reply";
|
||||
m_asyncDeviceAdditions.insert(newDeviceId, reply);
|
||||
return reply;
|
||||
}
|
||||
|
||||
if (status != DeviceManager::DeviceErrorNoError)
|
||||
return createErrorReply(HttpReply::InternalServerError);
|
||||
|
||||
return createSuccessReply();
|
||||
QVariantMap result;
|
||||
result.insert("deviceId", newDeviceId);
|
||||
HttpReply *reply = createSuccessReply();
|
||||
reply->setPayload(QJsonDocument::fromVariant(result).toJson());
|
||||
return reply;
|
||||
}
|
||||
|
||||
HttpReply *DevicesResource::editDevice(Device *device, const QByteArray &payload) const
|
||||
@ -302,6 +323,7 @@ HttpReply *DevicesResource::editDevice(Device *device, const QByteArray &payload
|
||||
|
||||
if (status == DeviceManager::DeviceErrorAsync) {
|
||||
HttpReply *reply = createAsyncReply();
|
||||
qCDebug(dcRest) << "Device edit async reply";
|
||||
m_asyncEditDevice.insert(device, reply);
|
||||
return reply;
|
||||
}
|
||||
@ -319,9 +341,11 @@ void DevicesResource::actionExecuted(const ActionId &actionId, DeviceManager::De
|
||||
|
||||
HttpReply *reply = m_asyncActionExecutions.take(actionId);
|
||||
if (status == DeviceManager::DeviceErrorNoError) {
|
||||
qCDebug(dcRest) << "Action execution finished successfully";
|
||||
reply->setHttpStatusCode(HttpReply::Ok);
|
||||
} else {
|
||||
reply->setHttpStatusCode(HttpReply::BadRequest);
|
||||
qCDebug(dcRest) << "Action execution finished with error" << status;
|
||||
reply->setHttpStatusCode(HttpReply::InternalServerError);
|
||||
}
|
||||
|
||||
reply->finished();
|
||||
@ -329,33 +353,36 @@ void DevicesResource::actionExecuted(const ActionId &actionId, DeviceManager::De
|
||||
|
||||
void DevicesResource::deviceSetupFinished(Device *device, DeviceManager::DeviceError status)
|
||||
{
|
||||
if (!m_asyncEditDevice.contains(device))
|
||||
if (!m_asyncDeviceAdditions.contains(device->id()))
|
||||
return; // Not the device we are waiting for.
|
||||
|
||||
HttpReply *reply = m_asyncEditDevice.take(device);
|
||||
HttpReply *reply = m_asyncDeviceAdditions.take(device->id());
|
||||
if (status == DeviceManager::DeviceErrorNoError) {
|
||||
qCDebug(dcRest) << "Device setup finished successfully";
|
||||
reply->setHttpStatusCode(HttpReply::Ok);
|
||||
} else {
|
||||
reply->setHttpStatusCode(HttpReply::BadRequest);
|
||||
qCDebug(dcRest) << "Device setup finished with error" << status;
|
||||
reply->setHttpStatusCode(HttpReply::InternalServerError);
|
||||
}
|
||||
|
||||
QVariantMap result;
|
||||
result.insert("deviceId", device->id());
|
||||
|
||||
reply->setPayload(QJsonDocument::fromVariant(result).toJson());
|
||||
reply->finished();
|
||||
}
|
||||
|
||||
void DevicesResource::deviceEditFinished(Device *device, DeviceManager::DeviceError status)
|
||||
{
|
||||
if (!m_asynDeviceAdditions.contains(device->id()))
|
||||
if (!m_asyncEditDevice.contains(device))
|
||||
return; // Not the device we are waiting for.
|
||||
|
||||
HttpReply *reply = m_asyncEditDevice.take(device);
|
||||
if (status == DeviceManager::DeviceErrorNoError) {
|
||||
qCDebug(dcRest) << "Device edit finished successfully";
|
||||
reply->setHttpStatusCode(HttpReply::Ok);
|
||||
} else {
|
||||
reply->setHttpStatusCode(HttpReply::BadRequest);
|
||||
qCDebug(dcRest) << "Device edit finished with error" << status;
|
||||
reply->setHttpStatusCode(HttpReply::InternalServerError);
|
||||
}
|
||||
|
||||
reply->finished();
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
#include "jsontypes.h"
|
||||
#include "restresource.h"
|
||||
#include "network/httpreply.h"
|
||||
#include "httpreply.h"
|
||||
|
||||
class HttpRequest;
|
||||
|
||||
@ -44,7 +44,7 @@ public:
|
||||
|
||||
private:
|
||||
mutable QHash<ActionId, HttpReply *> m_asyncActionExecutions;
|
||||
mutable QHash<DeviceId, HttpReply *> m_asynDeviceAdditions;
|
||||
mutable QHash<DeviceId, HttpReply *> m_asyncDeviceAdditions;
|
||||
mutable QHash<Device *, HttpReply *> m_asyncEditDevice;
|
||||
mutable QHash<QUuid, HttpReply *> m_asyncPairingRequests;
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "logsresource.h"
|
||||
#include "network/httprequest.h"
|
||||
#include "httprequest.h"
|
||||
|
||||
namespace guhserver {
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
#include "jsontypes.h"
|
||||
#include "restresource.h"
|
||||
#include "network/httpreply.h"
|
||||
#include "httpreply.h"
|
||||
|
||||
class HttpRequest;
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "pluginsresource.h"
|
||||
#include "network/httprequest.h"
|
||||
#include "httprequest.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "guhcore.h"
|
||||
|
||||
@ -30,6 +30,7 @@ namespace guhserver {
|
||||
PluginsResource::PluginsResource(QObject *parent) :
|
||||
RestResource(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QString PluginsResource::name() const
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
#include "jsontypes.h"
|
||||
#include "restresource.h"
|
||||
#include "network/httpreply.h"
|
||||
#include "httpreply.h"
|
||||
|
||||
class HttpRequest;
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "restresource.h"
|
||||
#include "network/httprequest.h"
|
||||
#include "httprequest.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "guhcore.h"
|
||||
|
||||
@ -47,6 +47,7 @@ HttpReply *RestResource::createSuccessReply()
|
||||
HttpReply *RestResource::createErrorReply(const HttpReply::HttpStatusCode &statusCode)
|
||||
{
|
||||
HttpReply *reply = new HttpReply(statusCode, HttpReply::TypeSync);
|
||||
reply->setPayload(QByteArray::number(reply->httpStatusCode()) + " " + reply->httpReasonPhrase());
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
@ -24,10 +24,9 @@
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
|
||||
#include "network/httpreply.h"
|
||||
#include "httpreply.h"
|
||||
#include "httprequest.h"
|
||||
|
||||
class HttpRequest;
|
||||
class HttpReply;
|
||||
class QVariant;
|
||||
|
||||
namespace guhserver {
|
||||
@ -54,6 +53,10 @@ private:
|
||||
virtual HttpReply *proccessPutRequest(const HttpRequest &request, const QStringList &urlTokens);
|
||||
virtual HttpReply *proccessPostRequest(const HttpRequest &request, const QStringList &urlTokens);
|
||||
|
||||
QHash<QPair<HttpRequest::RequestMethod, QString>, QString> m_descriptions;
|
||||
QHash<QString, QVariantMap> m_params;
|
||||
QHash<QString, QVariantMap> m_returns;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
|
||||
#include "restserver.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "network/httprequest.h"
|
||||
#include "network/httpreply.h"
|
||||
#include "httprequest.h"
|
||||
#include "httpreply.h"
|
||||
#include "guhcore.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
@ -64,7 +64,6 @@ void RestServer::clientDisconnected(const QUuid &clientId)
|
||||
void RestServer::processHttpRequest(const QUuid &clientId, const HttpRequest &request)
|
||||
{
|
||||
qCDebug(dcRest) << "Process HTTP request";
|
||||
qCDebug(dcRest) << request;
|
||||
|
||||
QStringList urlTokens = request.url().path().split("/");
|
||||
urlTokens.removeAll(QString());
|
||||
@ -86,8 +85,8 @@ void RestServer::processHttpRequest(const QUuid &clientId, const HttpRequest &re
|
||||
reply->setClientId(clientId);
|
||||
if (reply->type() == HttpReply::TypeAsync) {
|
||||
connect(reply, &HttpReply::finished, this, &RestServer::asyncReplyFinished);
|
||||
reply->startWait();
|
||||
m_asyncReplies.insert(clientId, reply);
|
||||
reply->startWait();
|
||||
return;
|
||||
}
|
||||
m_webserver->sendHttpReply(reply);
|
||||
@ -100,8 +99,8 @@ void RestServer::processHttpRequest(const QUuid &clientId, const HttpRequest &re
|
||||
reply->setClientId(clientId);
|
||||
if (reply->type() == HttpReply::TypeAsync) {
|
||||
connect(reply, &HttpReply::finished, this, &RestServer::asyncReplyFinished);
|
||||
reply->startWait();
|
||||
m_asyncReplies.insert(clientId, reply);
|
||||
reply->startWait();
|
||||
return;
|
||||
}
|
||||
m_webserver->sendHttpReply(reply);
|
||||
@ -114,8 +113,8 @@ void RestServer::processHttpRequest(const QUuid &clientId, const HttpRequest &re
|
||||
reply->setClientId(clientId);
|
||||
if (reply->type() == HttpReply::TypeAsync) {
|
||||
connect(reply, &HttpReply::finished, this, &RestServer::asyncReplyFinished);
|
||||
reply->startWait();
|
||||
m_asyncReplies.insert(clientId, reply);
|
||||
reply->startWait();
|
||||
return;
|
||||
}
|
||||
m_webserver->sendHttpReply(reply);
|
||||
@ -128,8 +127,8 @@ void RestServer::processHttpRequest(const QUuid &clientId, const HttpRequest &re
|
||||
reply->setClientId(clientId);
|
||||
if (reply->type() == HttpReply::TypeAsync) {
|
||||
connect(reply, &HttpReply::finished, this, &RestServer::asyncReplyFinished);
|
||||
reply->startWait();
|
||||
m_asyncReplies.insert(clientId, reply);
|
||||
reply->startWait();
|
||||
return;
|
||||
}
|
||||
m_webserver->sendHttpReply(reply);
|
||||
@ -142,8 +141,8 @@ void RestServer::processHttpRequest(const QUuid &clientId, const HttpRequest &re
|
||||
reply->setClientId(clientId);
|
||||
if (reply->type() == HttpReply::TypeAsync) {
|
||||
connect(reply, &HttpReply::finished, this, &RestServer::asyncReplyFinished);
|
||||
reply->startWait();
|
||||
m_asyncReplies.insert(clientId, reply);
|
||||
reply->startWait();
|
||||
return;
|
||||
}
|
||||
m_webserver->sendHttpReply(reply);
|
||||
@ -160,13 +159,10 @@ void RestServer::asyncReplyFinished()
|
||||
{
|
||||
HttpReply *reply = qobject_cast<HttpReply*>(sender());
|
||||
|
||||
qCDebug(dcWebServer) << "sending reply" << reply->data();
|
||||
qCDebug(dcWebServer) << "Async reply finished";
|
||||
|
||||
if (!reply->timedOut()) {
|
||||
reply->setHttpStatusCode(HttpReply::Ok);
|
||||
} else {
|
||||
if (reply->timedOut())
|
||||
reply->setHttpStatusCode(HttpReply::GatewayTimeout);
|
||||
}
|
||||
|
||||
m_webserver->sendHttpReply(reply);
|
||||
reply->deleteLater();
|
||||
|
||||
@ -19,7 +19,8 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "rulesresource.h"
|
||||
#include "network/httprequest.h"
|
||||
#include "httprequest.h"
|
||||
#include "typeutils.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "guhcore.h"
|
||||
|
||||
@ -46,6 +47,10 @@ HttpReply *RulesResource::proccessRequest(const HttpRequest &request, const QStr
|
||||
qCWarning(dcRest) << "Could not parse RuleId:" << urlTokens.at(3);
|
||||
return createErrorReply(HttpReply::BadRequest);
|
||||
}
|
||||
|
||||
if (GuhCore::instance()->findRule(m_ruleId).id().isNull())
|
||||
return createErrorReply(HttpReply::NotFound);
|
||||
|
||||
}
|
||||
|
||||
// check method
|
||||
@ -72,8 +77,16 @@ HttpReply *RulesResource::proccessGetRequest(const HttpRequest &request, const Q
|
||||
Q_UNUSED(request)
|
||||
|
||||
// GET /api/v1/rules
|
||||
if (urlTokens.count() == 3)
|
||||
return getRules();
|
||||
if (urlTokens.count() == 3) {
|
||||
// check if we should filter for rules containing a certain device
|
||||
DeviceId deviceId;
|
||||
if (request.url().hasQuery() && request.urlQuery().hasQueryItem("deviceId")) {
|
||||
deviceId = DeviceId(request.urlQuery().queryItemValue("deviceId"));
|
||||
if (deviceId.isNull())
|
||||
createErrorReply(HttpReply::BadRequest);
|
||||
}
|
||||
return getRules(deviceId);
|
||||
}
|
||||
|
||||
// GET /api/v1/rules/{ruleId}
|
||||
if (urlTokens.count() == 4)
|
||||
@ -85,48 +98,64 @@ HttpReply *RulesResource::proccessGetRequest(const HttpRequest &request, const Q
|
||||
HttpReply *RulesResource::proccessDeleteRequest(const HttpRequest &request, const QStringList &urlTokens)
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
Q_UNUSED(urlTokens)
|
||||
|
||||
// DELETE /api/v1/rules
|
||||
if (urlTokens.count() == 3)
|
||||
return createErrorReply(HttpReply::BadRequest);
|
||||
|
||||
// DELETE /api/v1/rules/{ruleId}
|
||||
if (urlTokens.count() == 4)
|
||||
return removeRule(m_ruleId);
|
||||
|
||||
return createErrorReply(HttpReply::NotImplemented);
|
||||
}
|
||||
|
||||
HttpReply *RulesResource::proccessPutRequest(const HttpRequest &request, const QStringList &urlTokens)
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
Q_UNUSED(urlTokens)
|
||||
// PUT /api/v1/rules
|
||||
if (urlTokens.count() == 3)
|
||||
return createErrorReply(HttpReply::BadRequest);
|
||||
|
||||
// PUT /api/v1/rules/{ruleId}
|
||||
if (urlTokens.count() == 4)
|
||||
return editRule(m_ruleId, request.payload());
|
||||
|
||||
return createErrorReply(HttpReply::NotImplemented);
|
||||
}
|
||||
|
||||
HttpReply *RulesResource::proccessPostRequest(const HttpRequest &request, const QStringList &urlTokens)
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
Q_UNUSED(urlTokens)
|
||||
{
|
||||
// POST /api/v1/rules
|
||||
if (urlTokens.count() == 3)
|
||||
return addRule(request.payload());
|
||||
|
||||
return createErrorReply(HttpReply::NotImplemented);
|
||||
}
|
||||
|
||||
HttpReply *RulesResource::getRules() const
|
||||
HttpReply *RulesResource::getRules(const DeviceId &deviceId) const
|
||||
{
|
||||
qCDebug(dcRest) << "Get rule descriptions";
|
||||
HttpReply *reply = createSuccessReply();
|
||||
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packRuleDescriptions()).toJson());
|
||||
return reply;
|
||||
}
|
||||
|
||||
Rule RulesResource::findRule(const RuleId &ruleId) const
|
||||
{
|
||||
foreach (const Rule &rule, GuhCore::instance()->rules()) {
|
||||
if (rule.id() == ruleId) {
|
||||
return rule;
|
||||
if (deviceId.isNull()) {
|
||||
qCDebug(dcRest) << "Get rule descriptions";
|
||||
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packRuleDescriptions()).toJson());
|
||||
} else {
|
||||
qCDebug(dcRest) << "Get rule descriptions which contain the device with id" << deviceId.toString();
|
||||
QList<RuleId> ruleIdsList = GuhCore::instance()->findRules(deviceId);
|
||||
QList<Rule> ruleList;
|
||||
foreach (const RuleId &ruleId, ruleIdsList) {
|
||||
Rule rule = GuhCore::instance()->findRule(ruleId);
|
||||
if (!rule.id().isNull())
|
||||
ruleList.append(rule);
|
||||
}
|
||||
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packRuleDescriptions(ruleList)).toJson());
|
||||
}
|
||||
return Rule();
|
||||
return reply;
|
||||
}
|
||||
|
||||
HttpReply *RulesResource::getRuleDetails(const RuleId &ruleId) const
|
||||
{
|
||||
Rule rule = findRule(ruleId);
|
||||
Rule rule = GuhCore::instance()->findRule(ruleId);
|
||||
if (rule.id().isNull())
|
||||
return createErrorReply(HttpReply::NotFound);
|
||||
|
||||
@ -136,5 +165,34 @@ HttpReply *RulesResource::getRuleDetails(const RuleId &ruleId) const
|
||||
return reply;
|
||||
}
|
||||
|
||||
HttpReply *RulesResource::removeRule(const RuleId &ruleId) const
|
||||
{
|
||||
qCDebug(dcRest) << "Remove rule with id" << ruleId.toString();
|
||||
|
||||
RuleEngine::RuleError status = GuhCore::instance()->removeRule(ruleId);
|
||||
|
||||
if (status == RuleEngine::RuleErrorNoError)
|
||||
return createSuccessReply();
|
||||
|
||||
return createErrorReply(HttpReply::InternalServerError);
|
||||
}
|
||||
|
||||
HttpReply *RulesResource::addRule(const QByteArray &payload) const
|
||||
{
|
||||
Q_UNUSED(payload)
|
||||
qCDebug(dcRest) << "Add new rule";
|
||||
|
||||
|
||||
return createErrorReply(HttpReply::NotImplemented);
|
||||
}
|
||||
|
||||
HttpReply *RulesResource::editRule(const RuleId &ruleId, const QByteArray &payload) const
|
||||
{
|
||||
Q_UNUSED(payload)
|
||||
qCDebug(dcRest) << "Edit rule with id" << ruleId;
|
||||
|
||||
return createErrorReply(HttpReply::NotImplemented);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
#include "jsontypes.h"
|
||||
#include "restresource.h"
|
||||
#include "network/httpreply.h"
|
||||
#include "httpreply.h"
|
||||
|
||||
class HttpRequest;
|
||||
|
||||
@ -52,17 +52,18 @@ private:
|
||||
HttpReply *proccessPostRequest(const HttpRequest &request, const QStringList &urlTokens) override;
|
||||
|
||||
// Get methods
|
||||
HttpReply *getRules() const;
|
||||
HttpReply *getRules(const DeviceId &deviceId) const;
|
||||
HttpReply *getRuleDetails(const RuleId &ruleId) const;
|
||||
|
||||
// Delete methods
|
||||
HttpReply *removeRule(const RuleId &ruleId) const;
|
||||
|
||||
// Post methods
|
||||
HttpReply *addRule(const QByteArray &payload) const;
|
||||
|
||||
// Put methods
|
||||
HttpReply *editRule(const RuleId &ruleId, const QByteArray &payload) const;
|
||||
|
||||
|
||||
Rule findRule(const RuleId &ruleId) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "vendorsresource.h"
|
||||
#include "network/httprequest.h"
|
||||
#include "httprequest.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "guhcore.h"
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
#include "jsontypes.h"
|
||||
#include "restresource.h"
|
||||
#include "network/httpreply.h"
|
||||
#include "httpreply.h"
|
||||
|
||||
class HttpRequest;
|
||||
|
||||
|
||||
@ -25,6 +25,8 @@ SOURCES += $$top_srcdir/server/guhcore.cpp \
|
||||
$$top_srcdir/server/transportinterface.cpp \
|
||||
$$top_srcdir/server/servermanager.cpp \
|
||||
$$top_srcdir/server/websocketserver.cpp \
|
||||
$$top_srcdir/server/httprequest.cpp \
|
||||
$$top_srcdir/server/httpreply.cpp \
|
||||
$$top_srcdir/server/rest/restserver.cpp \
|
||||
$$top_srcdir/server/rest/restresource.cpp \
|
||||
$$top_srcdir/server/rest/devicesresource.cpp \
|
||||
@ -57,6 +59,8 @@ HEADERS += $$top_srcdir/server/guhcore.h \
|
||||
$$top_srcdir/server/transportinterface.h \
|
||||
$$top_srcdir/server/servermanager.h \
|
||||
$$top_srcdir/server/websocketserver.h \
|
||||
$$top_srcdir/server/httprequest.h \
|
||||
$$top_srcdir/server/httpreply.h \
|
||||
$$top_srcdir/server/rest/restserver.h \
|
||||
$$top_srcdir/server/rest/restresource.h \
|
||||
$$top_srcdir/server/rest/devicesresource.h \
|
||||
|
||||
@ -21,8 +21,8 @@
|
||||
#include "webserver.h"
|
||||
#include "loggingcategories.h"
|
||||
#include "guhsettings.h"
|
||||
#include "network/httpreply.h"
|
||||
#include "network/httprequest.h"
|
||||
#include "httpreply.h"
|
||||
#include "httprequest.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QTcpServer>
|
||||
@ -98,11 +98,11 @@ void WebServer::sendHttpReply(HttpReply *reply)
|
||||
|
||||
bool WebServer::verifyFile(QTcpSocket *socket, const QString &fileName)
|
||||
{
|
||||
QFileInfo checkFile(fileName);
|
||||
QFileInfo file(fileName);
|
||||
|
||||
// make shore the file exists
|
||||
if (!checkFile.exists()) {
|
||||
qCWarning(dcWebServer) << "requested file" << checkFile.fileName() << "does not exist.";
|
||||
if (!file.exists()) {
|
||||
qCWarning(dcWebServer) << "requested file" << file.fileName() << "does not exist.";
|
||||
HttpReply reply(HttpReply::NotFound);
|
||||
reply.setPayload("404 Not found.");
|
||||
reply.packReply();
|
||||
@ -111,8 +111,8 @@ bool WebServer::verifyFile(QTcpSocket *socket, const QString &fileName)
|
||||
}
|
||||
|
||||
// make shore the file is in the public directory
|
||||
if (!checkFile.canonicalFilePath().startsWith(m_webinterfaceDir.path())) {
|
||||
qCWarning(dcWebServer) << "requested file" << checkFile.fileName() << "is outside the public folder.";
|
||||
if (!file.canonicalFilePath().startsWith(m_webinterfaceDir.path())) {
|
||||
qCWarning(dcWebServer) << "requested file" << file.fileName() << "is outside the public folder.";
|
||||
HttpReply reply(HttpReply::Forbidden);
|
||||
reply.setPayload("403 Forbidden.");
|
||||
reply.packReply();
|
||||
@ -122,8 +122,8 @@ bool WebServer::verifyFile(QTcpSocket *socket, const QString &fileName)
|
||||
}
|
||||
|
||||
// make shore we can read the file
|
||||
if (!checkFile.isReadable()) {
|
||||
qCWarning(dcWebServer) << "requested file" << checkFile.fileName() << "is not readable.";
|
||||
if (!file.isReadable()) {
|
||||
qCWarning(dcWebServer) << "requested file" << file.fileName() << "is not readable.";
|
||||
HttpReply reply(HttpReply::Forbidden);
|
||||
reply.setPayload("403 Forbidden. Page not readable.");
|
||||
reply.packReply();
|
||||
@ -143,7 +143,7 @@ QString WebServer::fileName(const QString &query)
|
||||
fileName = query;
|
||||
}
|
||||
|
||||
return QFileInfo(m_webinterfaceDir.path() + fileName).canonicalFilePath();
|
||||
return m_webinterfaceDir.path() + fileName;
|
||||
}
|
||||
|
||||
void WebServer::writeData(QTcpSocket *socket, const QByteArray &data)
|
||||
@ -167,6 +167,8 @@ void WebServer::onNewConnection()
|
||||
|
||||
connect(socket, &QTcpSocket::readyRead, this, &WebServer::readClient);
|
||||
connect(socket, &QTcpSocket::disconnected, this, &WebServer::onDisconnected);
|
||||
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
|
||||
|
||||
qCDebug(dcConnection) << "Webserver client connected" << socket->peerName() << socket->peerAddress().toString() << socket->peerPort();
|
||||
|
||||
emit clientConnected(clientId);
|
||||
@ -183,16 +185,32 @@ void WebServer::readClient()
|
||||
// check client
|
||||
if (clientId.isNull()) {
|
||||
qCWarning(dcWebServer) << "Client not recognized";
|
||||
socket->close();
|
||||
socket->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
// read HTTP request
|
||||
HttpRequest request = HttpRequest(socket->readAll());
|
||||
QByteArray data = socket->readAll();
|
||||
|
||||
HttpRequest request;
|
||||
if (m_incompleteRequests.contains(socket)) {
|
||||
request = m_incompleteRequests.take(socket);
|
||||
request.appendData(data);
|
||||
} else {
|
||||
request = HttpRequest(data);
|
||||
}
|
||||
|
||||
if (!request.isComplete()) {
|
||||
qCWarning(dcWebServer) << "Hash incomplete message.";
|
||||
m_incompleteRequests.insert(socket, request);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!request.isValid()) {
|
||||
qCWarning(dcWebServer) << "Got invalid request.";
|
||||
HttpReply reply(HttpReply::BadRequest);
|
||||
reply.setPayload("400 Bad Request.");
|
||||
reply.packReply();
|
||||
writeData(socket, reply.data());
|
||||
return;
|
||||
}
|
||||
@ -202,24 +220,23 @@ void WebServer::readClient()
|
||||
qCWarning(dcWebServer) << "HTTP version is not supported." ;
|
||||
HttpReply reply(HttpReply::HttpVersionNotSupported);
|
||||
reply.setPayload("505 HTTP version is not supported.");
|
||||
reply.packReply();
|
||||
writeData(socket, reply.data());
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcWebServer) << QString("Got valid request from %1:%2").arg(socket->peerAddress().toString()).arg(socket->peerPort());
|
||||
qCDebug(dcWebServer) << request.methodString() << request.url().path();
|
||||
|
||||
// verify method
|
||||
if (request.method() == HttpRequest::Unhandled) {
|
||||
HttpReply reply(HttpReply::MethodNotAllowed);
|
||||
reply.setHeader(HttpReply::AllowHeader, "GET, PUT, POST, DELETE");
|
||||
reply.setPayload("405 Method not allowed.");
|
||||
reply.packReply();
|
||||
writeData(socket, reply.data());
|
||||
return;
|
||||
}
|
||||
|
||||
// verify query
|
||||
// verify API query
|
||||
if (request.url().path().startsWith("/api/v1")) {
|
||||
emit httpRequestReady(clientId, request);
|
||||
return;
|
||||
@ -227,10 +244,11 @@ void WebServer::readClient()
|
||||
|
||||
// request for a file...
|
||||
if (request.method() == HttpRequest::Get) {
|
||||
if (!verifyFile(socket, fileName(request.urlQuery().query())))
|
||||
QString path = fileName(request.url().path());
|
||||
if (!verifyFile(socket, path))
|
||||
return;
|
||||
|
||||
QFile file(fileName(request.urlQuery().query()));
|
||||
QFile file(path);
|
||||
if (file.open(QFile::ReadOnly | QFile::Truncate)) {
|
||||
qCDebug(dcWebServer) << "load file" << file.fileName();
|
||||
HttpReply reply(HttpReply::Ok);
|
||||
@ -238,7 +256,6 @@ void WebServer::readClient()
|
||||
reply.setHeader(HttpReply::ContentTypeHeader, "text/html; charset=\"utf-8\";");
|
||||
}
|
||||
reply.setPayload(file.readAll());
|
||||
reply.packReply();
|
||||
writeData(socket, reply.data());
|
||||
return;
|
||||
}
|
||||
@ -248,7 +265,6 @@ void WebServer::readClient()
|
||||
qCWarning(dcWebServer) << "Unknown message received. Respond client with 501: Not Implemented.";
|
||||
HttpReply reply(HttpReply::NotImplemented);
|
||||
reply.setPayload("501 Not implemented.");
|
||||
reply.packReply();
|
||||
writeData(socket, reply.data());
|
||||
}
|
||||
|
||||
@ -260,11 +276,18 @@ void WebServer::onDisconnected()
|
||||
// clean up
|
||||
QUuid clientId = m_clientList.key(socket);
|
||||
m_clientList.remove(clientId);
|
||||
m_incompleteRequests.remove(socket);
|
||||
socket->deleteLater();
|
||||
|
||||
emit clientDisconnected(clientId);
|
||||
}
|
||||
|
||||
void WebServer::onError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
QTcpSocket* socket = qobject_cast<QTcpSocket *>(sender());
|
||||
qWarning(dcWebServer) << "Client socket error" << socket->peerAddress() << error << socket->errorString();
|
||||
}
|
||||
|
||||
bool WebServer::startServer()
|
||||
{
|
||||
if (!m_server->listen(QHostAddress::Any, m_port)) {
|
||||
|
||||
@ -25,12 +25,11 @@
|
||||
#include <QHash>
|
||||
#include <QDir>
|
||||
#include <QUuid>
|
||||
|
||||
#include <QTcpSocket>
|
||||
|
||||
#include "transportinterface.h"
|
||||
|
||||
class QTcpServer;
|
||||
class QTcpSocket;
|
||||
class HttpRequest;
|
||||
class HttpReply;
|
||||
|
||||
@ -56,6 +55,7 @@ private:
|
||||
QTcpServer *m_server;
|
||||
|
||||
QHash<QUuid, QTcpSocket *> m_clientList;
|
||||
QHash<QTcpSocket *, HttpRequest> m_incompleteRequests;
|
||||
|
||||
bool m_enabled;
|
||||
qint16 m_port;
|
||||
@ -74,6 +74,7 @@ private slots:
|
||||
void onNewConnection();
|
||||
void readClient();
|
||||
void onDisconnected();
|
||||
void onError(QAbstractSocket::SocketError error);
|
||||
|
||||
public slots:
|
||||
bool startServer() override;
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
#include <QNetworkReply>
|
||||
#include <QCoreApplication>
|
||||
#include <QJsonDocument>
|
||||
#include <QHttpPart>
|
||||
#include <QMetaType>
|
||||
|
||||
using namespace guhserver;
|
||||
@ -43,10 +44,18 @@ class TestRestDevices: public GuhTestBase
|
||||
private slots:
|
||||
void getConfiguredDevices();
|
||||
|
||||
void addConfiguredDevice_data();
|
||||
void addConfiguredDevice();
|
||||
|
||||
void executeAction_data();
|
||||
void executeAction();
|
||||
|
||||
void getStateValue_data();
|
||||
void getStateValue();
|
||||
|
||||
private:
|
||||
// for debugging
|
||||
void printResponse(QNetworkReply *reply);
|
||||
void printResponse(QNetworkReply *reply, const QByteArray &data);
|
||||
|
||||
};
|
||||
|
||||
@ -55,32 +64,286 @@ void TestRestDevices::getConfiguredDevices()
|
||||
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
|
||||
QSignalSpy clientSpy(nam, SIGNAL(finished(QNetworkReply*)));
|
||||
|
||||
// Get all devices
|
||||
QNetworkRequest request;
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
|
||||
request.setUrl(QUrl("http://localhost:3000/api/v1/devices"));
|
||||
QNetworkReply *reply;
|
||||
|
||||
reply = nam->get(request);
|
||||
clientSpy.wait(200);
|
||||
clientSpy.wait();
|
||||
QVERIFY2(clientSpy.count() == 1, "expected exactly 1 response from webserver");
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
reply->deleteLater();
|
||||
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error);
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
QCOMPARE(error.error, QJsonParseError::NoError);
|
||||
QVariantList deviceList = jsonDoc.toVariant().toList();
|
||||
QCOMPARE(deviceList.count(), 3);
|
||||
QVERIFY2(deviceList.count() >= 2, "not enought devices.");
|
||||
|
||||
// Get each of thouse devices individualy
|
||||
foreach (const QVariant &device, deviceList) {
|
||||
QVariantMap deviceMap = device.toMap();
|
||||
QNetworkRequest request;
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
|
||||
request.setUrl(QUrl(QString("http://localhost:3000/api/v1/devices/%1").arg(deviceMap.value("id").toString())));
|
||||
clientSpy.clear();
|
||||
QNetworkReply *reply = nam->get(request);
|
||||
clientSpy.wait();
|
||||
QVERIFY2(clientSpy.count() == 1, "expected exactly 1 response from webserver");
|
||||
|
||||
jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error);
|
||||
QCOMPARE(error.error, QJsonParseError::NoError);
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
nam->deleteLater();
|
||||
}
|
||||
|
||||
void TestRestDevices::addConfiguredDevice_data()
|
||||
{
|
||||
QTest::addColumn<DeviceClassId>("deviceClassId");
|
||||
QTest::addColumn<QVariantList>("deviceParams");
|
||||
QTest::addColumn<int>("expectedStatusCode");
|
||||
|
||||
QVariantMap nameParam;
|
||||
nameParam.insert("name", "name");
|
||||
nameParam.insert("value", "Test Mockdevice");
|
||||
QVariantMap httpportParam;
|
||||
httpportParam.insert("name", "httpport");
|
||||
httpportParam.insert("value", m_mockDevice1Port - 1);
|
||||
QVariantMap asyncParam;
|
||||
asyncParam.insert("name", "async");
|
||||
asyncParam.insert("value", true);
|
||||
QVariantMap notAsyncParam;
|
||||
notAsyncParam.insert("name", "async");
|
||||
notAsyncParam.insert("value", false);
|
||||
QVariantMap notBrokenParam;
|
||||
notBrokenParam.insert("name", "broken");
|
||||
notBrokenParam.insert("value", false);
|
||||
QVariantMap brokenParam;
|
||||
brokenParam.insert("name", "broken");
|
||||
brokenParam.insert("value", true);
|
||||
|
||||
QVariantList deviceParams;
|
||||
|
||||
deviceParams.clear(); deviceParams << nameParam << httpportParam << notAsyncParam << notBrokenParam;
|
||||
QTest::newRow("User, JustAdd") << mockDeviceClassId << deviceParams << 200;
|
||||
|
||||
deviceParams.clear(); deviceParams << nameParam << httpportParam << asyncParam << notBrokenParam;
|
||||
QTest::newRow("User, JustAdd, Async") << mockDeviceClassId << deviceParams << 200;
|
||||
|
||||
QTest::newRow("Invalid DeviceClassId") << DeviceClassId::createDeviceClassId() << deviceParams << 500;
|
||||
|
||||
deviceParams.clear(); deviceParams << nameParam << httpportParam << brokenParam;
|
||||
QTest::newRow("Setup failure") << mockDeviceClassId << deviceParams << 500;
|
||||
|
||||
deviceParams.clear(); deviceParams << nameParam << httpportParam << asyncParam << brokenParam;
|
||||
QTest::newRow("Setup failure, Async") << mockDeviceClassId << deviceParams << 500;
|
||||
|
||||
QVariantList invalidDeviceParams;
|
||||
QTest::newRow("User, JustAdd, missing params") << mockDeviceClassId << invalidDeviceParams << 500;
|
||||
|
||||
QVariantMap fakeparam;
|
||||
fakeparam.insert("name", "tropptth");
|
||||
invalidDeviceParams.append(fakeparam);
|
||||
QTest::newRow("User, JustAdd, invalid param") << mockDeviceClassId << invalidDeviceParams << 500;
|
||||
|
||||
fakeparam.insert("value", "buhuu");
|
||||
invalidDeviceParams.clear();
|
||||
invalidDeviceParams.append(fakeparam);
|
||||
QTest::newRow("User, JustAdd, wrong param") << mockDeviceClassId << invalidDeviceParams << 500;
|
||||
}
|
||||
|
||||
void TestRestDevices::addConfiguredDevice()
|
||||
{
|
||||
QFETCH(DeviceClassId, deviceClassId);
|
||||
QFETCH(QVariantList, deviceParams);
|
||||
QFETCH(int, expectedStatusCode);
|
||||
|
||||
QVariantMap params;
|
||||
params.insert("deviceClassId", deviceClassId);
|
||||
params.insert("deviceParams", deviceParams);
|
||||
|
||||
QNetworkAccessManager *nam = new QNetworkAccessManager();
|
||||
QSignalSpy clientSpy(nam, SIGNAL(finished(QNetworkReply*)));
|
||||
|
||||
// Get all devices
|
||||
QNetworkRequest request;
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
request.setUrl(QUrl("http://localhost:3000/api/v1/devices"));
|
||||
|
||||
QByteArray payload = QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact);
|
||||
qDebug() << "sending" << payload;
|
||||
|
||||
QNetworkReply *reply = nam->post(request, payload);
|
||||
clientSpy.wait();
|
||||
QCOMPARE(clientSpy.count(), 1);
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
|
||||
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
QCOMPARE(statusCode, expectedStatusCode);
|
||||
|
||||
reply->deleteLater();
|
||||
|
||||
if (expectedStatusCode == 200) {
|
||||
// remove added device
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
|
||||
QCOMPARE(error.error, QJsonParseError::NoError);
|
||||
QVariantMap response = jsonDoc.toVariant().toMap();
|
||||
|
||||
DeviceId deviceId = DeviceId(response.value("deviceId").toString());
|
||||
|
||||
request.setUrl(QUrl(QString("http://localhost:3000/api/v1/devices/%1").arg(deviceId.toString())));
|
||||
clientSpy.clear();
|
||||
reply = nam->deleteResource(request);
|
||||
clientSpy.wait();
|
||||
QVERIFY2(clientSpy.count() == 1, "expected exactly 1 response from webserver");
|
||||
statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
reply->deleteLater();
|
||||
QCOMPARE(statusCode, 200);
|
||||
}
|
||||
nam->deleteLater();
|
||||
}
|
||||
|
||||
void TestRestDevices::executeAction_data()
|
||||
{
|
||||
QTest::addColumn<DeviceId>("deviceId");
|
||||
QTest::addColumn<ActionTypeId>("actionTypeId");
|
||||
QTest::addColumn<QVariantList>("actionParams");
|
||||
QTest::addColumn<int>("expectedStatusCode");
|
||||
|
||||
QVariantList params;
|
||||
QVariantMap param1;
|
||||
param1.insert("name", "mockActionParam1");
|
||||
param1.insert("value", 5);
|
||||
params.append(param1);
|
||||
QVariantMap param2;
|
||||
param2.insert("name", "mockActionParam2");
|
||||
param2.insert("value", true);
|
||||
params.append(param2);
|
||||
|
||||
QTest::newRow("valid action") << m_mockDeviceId << mockActionIdWithParams << params << 200;
|
||||
QTest::newRow("invalid deviceId") << DeviceId::createDeviceId() << mockActionIdWithParams << params << 404;
|
||||
QTest::newRow("invalid actionTypeId") << m_mockDeviceId << ActionTypeId::createActionTypeId() << params << 404;
|
||||
QTest::newRow("missing params") << m_mockDeviceId << mockActionIdWithParams << QVariantList() << 500;
|
||||
QTest::newRow("async action") << m_mockDeviceId << mockActionIdAsync << QVariantList() << 200;
|
||||
QTest::newRow("broken action") << m_mockDeviceId << mockActionIdFailing << QVariantList() << 500;
|
||||
QTest::newRow("async broken action") << m_mockDeviceId << mockActionIdAsyncFailing << QVariantList() << 500;
|
||||
}
|
||||
|
||||
void TestRestDevices::executeAction()
|
||||
{
|
||||
QFETCH(DeviceId, deviceId);
|
||||
QFETCH(ActionTypeId, actionTypeId);
|
||||
QFETCH(QVariantList, actionParams);
|
||||
QFETCH(int, expectedStatusCode);
|
||||
|
||||
// execute action
|
||||
QNetworkAccessManager nam;
|
||||
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
|
||||
|
||||
QVariantMap payloadMap;
|
||||
payloadMap.insert("params", actionParams);
|
||||
|
||||
QNetworkRequest request(QUrl(QString("http://localhost:3000/api/v1/devices/%1/execute/%2").arg(deviceId.toString()).arg(actionTypeId.toString())));
|
||||
spy.clear();
|
||||
QNetworkReply *reply = nam.post(request, QJsonDocument::fromVariant(payloadMap).toJson(QJsonDocument::Compact));
|
||||
spy.wait();
|
||||
QCOMPARE(spy.count(), 1);
|
||||
|
||||
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
QCOMPARE(statusCode, expectedStatusCode);
|
||||
reply->deleteLater();
|
||||
|
||||
// Fetch action execution history from mock device
|
||||
spy.clear();
|
||||
request = QNetworkRequest(QUrl(QString("http://localhost:%1/actionhistory").arg(m_mockDevice1Port)));
|
||||
reply = nam.get(request);
|
||||
spy.wait();
|
||||
QCOMPARE(spy.count(), 1);
|
||||
QByteArray data = reply->readAll();
|
||||
reply->deleteLater();
|
||||
|
||||
// cleanup for the next run
|
||||
spy.clear();
|
||||
request.setUrl(QUrl(QString("http://localhost:%1/clearactionhistory").arg(m_mockDevice1Port)));
|
||||
reply = nam.get(request);
|
||||
spy.wait();
|
||||
QCOMPARE(spy.count(), 1);
|
||||
reply->deleteLater();
|
||||
|
||||
spy.clear();
|
||||
request.setUrl(QUrl(QString("http://localhost:%1/actionhistory").arg(m_mockDevice1Port)));
|
||||
reply = nam.get(request);
|
||||
spy.wait();
|
||||
QCOMPARE(spy.count(), 1);
|
||||
reply->deleteLater();
|
||||
data = reply->readAll();
|
||||
}
|
||||
|
||||
void TestRestDevices::getStateValue_data()
|
||||
{
|
||||
QList<Device*> devices = GuhCore::instance()->findConfiguredDevices(mockDeviceClassId);
|
||||
QVERIFY2(devices.count() > 0, "There needs to be at least one configured Mock Device for this test");
|
||||
Device *device = devices.first();
|
||||
|
||||
QTest::addColumn<DeviceId>("deviceId");
|
||||
QTest::addColumn<StateTypeId>("stateTypeId");
|
||||
QTest::addColumn<int>("expectedStatusCode");
|
||||
|
||||
QTest::newRow("existing state") << device->id() << mockIntStateId << 200;
|
||||
QTest::newRow("all states") << device->id() << StateTypeId() << 200;
|
||||
QTest::newRow("invalid device") << DeviceId::createDeviceId() << mockIntStateId << 404;
|
||||
QTest::newRow("invalid statetype") << device->id() << StateTypeId::createStateTypeId() << 404;
|
||||
}
|
||||
|
||||
void TestRestDevices::getStateValue()
|
||||
{
|
||||
QFETCH(DeviceId, deviceId);
|
||||
QFETCH(StateTypeId, stateTypeId);
|
||||
QFETCH(int, expectedStatusCode);
|
||||
|
||||
QNetworkAccessManager *nam = new QNetworkAccessManager();
|
||||
QSignalSpy clientSpy(nam, SIGNAL(finished(QNetworkReply*)));
|
||||
|
||||
QNetworkRequest request;
|
||||
|
||||
if (!stateTypeId.isNull()) {
|
||||
request.setUrl(QUrl(QString("http://localhost:3000/api/v1/devices/%1/states/%2").arg(deviceId.toString()).arg(stateTypeId.toString())));
|
||||
} else {
|
||||
// Get all states
|
||||
request.setUrl(QUrl(QString("http://localhost:3000/api/v1/devices/%1/states").arg(deviceId.toString())));
|
||||
}
|
||||
qDebug() << request.url();
|
||||
|
||||
QNetworkReply *reply = nam->get(request);
|
||||
clientSpy.wait();
|
||||
QCOMPARE(clientSpy.count(), 1);
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
qDebug() << data;
|
||||
|
||||
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
QCOMPARE(statusCode, expectedStatusCode);
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void TestRestDevices::printResponse(QNetworkReply *reply)
|
||||
void TestRestDevices::printResponse(QNetworkReply *reply, const QByteArray &data)
|
||||
{
|
||||
qDebug() << "-------------------------------";
|
||||
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||
foreach (const QNetworkReply::RawHeaderPair &headerPair, reply->rawHeaderPairs()) {
|
||||
qDebug() << headerPair.first << ":" << headerPair.second;
|
||||
}
|
||||
qDebug() << "-------------------------------";
|
||||
qDebug() << reply->readAll();
|
||||
qDebug() << data;
|
||||
qDebug() << "-------------------------------";
|
||||
|
||||
}
|
||||
|
||||
#include "testrestdevices.moc"
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
#include <QNetworkReply>
|
||||
#include <QCoreApplication>
|
||||
#include <QMetaType>
|
||||
#include <QByteArray>
|
||||
|
||||
using namespace guhserver;
|
||||
|
||||
@ -42,9 +43,14 @@ class TestWebserver: public GuhTestBase
|
||||
private slots:
|
||||
void httpVersion();
|
||||
|
||||
void multiPackageMessage();
|
||||
|
||||
void checkAllowedMethodCall_data();
|
||||
void checkAllowedMethodCall();
|
||||
|
||||
void badRequests_data();
|
||||
void badRequests();
|
||||
|
||||
void getFiles_data();
|
||||
void getFiles();
|
||||
|
||||
@ -63,7 +69,11 @@ void TestWebserver::httpVersion()
|
||||
|
||||
QSignalSpy clientSpy(socket, SIGNAL(readyRead()));
|
||||
|
||||
socket->write("GET /hello/guh HTTP/1.0\r\n\r\n");
|
||||
QByteArray requestData;
|
||||
requestData.append("GET /hello/guh HTTP/1.0\r\n");
|
||||
requestData.append("User-Agent: guh webserver test\r\n\r\n");
|
||||
|
||||
socket->write(requestData);
|
||||
bool filesWritten = socket->waitForBytesWritten(500);
|
||||
QVERIFY2(filesWritten, "could not write to webserver.");
|
||||
|
||||
@ -86,6 +96,59 @@ void TestWebserver::httpVersion()
|
||||
socket->deleteLater();
|
||||
}
|
||||
|
||||
void TestWebserver::multiPackageMessage()
|
||||
{
|
||||
|
||||
QTcpSocket *socket = new QTcpSocket(this);
|
||||
socket->connectToHost(QHostAddress("127.0.0.1"), 3000);
|
||||
bool connected = socket->waitForConnected(1000);
|
||||
QVERIFY2(connected, "could not connect to webserver.");
|
||||
|
||||
QSignalSpy clientSpy(socket, SIGNAL(readyRead()));
|
||||
|
||||
QByteArray requestData;
|
||||
requestData.append("PUT / HTTP/1.1\r\n");
|
||||
requestData.append("User-Agent: webserver test\r\n");
|
||||
requestData.append("Content-Length: 42\r\n");
|
||||
requestData.append("\r\n");
|
||||
requestData.append("This message");
|
||||
|
||||
socket->write(requestData);
|
||||
bool filesWritten = socket->waitForBytesWritten();
|
||||
QVERIFY2(filesWritten, "could not write to webserver.");
|
||||
|
||||
socket->write(QByteArray(" was sent"));
|
||||
filesWritten = socket->waitForBytesWritten();
|
||||
QVERIFY2(filesWritten, "could not write to webserver.");
|
||||
|
||||
socket->write(QByteArray(" in four TCP"));
|
||||
filesWritten = socket->waitForBytesWritten();
|
||||
QVERIFY2(filesWritten, "could not write to webserver.");
|
||||
|
||||
socket->write(QByteArray("packages. "));
|
||||
filesWritten = socket->waitForBytesWritten();
|
||||
QVERIFY2(filesWritten, "could not write to webserver.");
|
||||
|
||||
clientSpy.wait(500);
|
||||
QVERIFY2(clientSpy.count() == 1, "expected exactly 1 response from webserver");
|
||||
QByteArray data = socket->readAll();
|
||||
QVERIFY2(!data.isEmpty(), "got no response");
|
||||
|
||||
QStringList lines = QString(data).split("\r\n");
|
||||
QStringList firstLineTokens = lines.first().split(QRegExp("[ \r\n][ \r\n]*"));
|
||||
|
||||
QVERIFY2(firstLineTokens.isEmpty() || firstLineTokens.count() > 2, "could not get tokens of first line");
|
||||
|
||||
bool ok = false;
|
||||
int statusCode = firstLineTokens.at(1).toInt(&ok);
|
||||
QVERIFY2(ok, "Could not convert statuscode from response to int");
|
||||
QCOMPARE(statusCode, 501);
|
||||
|
||||
socket->close();
|
||||
socket->deleteLater();
|
||||
|
||||
}
|
||||
|
||||
void TestWebserver::checkAllowedMethodCall_data()
|
||||
{
|
||||
QTest::addColumn<QString>("method");
|
||||
@ -141,6 +204,60 @@ void TestWebserver::checkAllowedMethodCall()
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void TestWebserver::badRequests_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("request");
|
||||
QTest::addColumn<int>("expectedStatusCode");
|
||||
|
||||
QByteArray wrongContentLength;
|
||||
wrongContentLength.append("PUT / HTTP/1.1\r\n");
|
||||
wrongContentLength.append("User-Agent: webserver test\r\n");
|
||||
wrongContentLength.append("Content-Length: 1\r\n");
|
||||
wrongContentLength.append("\r\n");
|
||||
wrongContentLength.append("longer content than told in the header");
|
||||
|
||||
|
||||
|
||||
|
||||
QTest::newRow("wrong content length") << wrongContentLength << 400;
|
||||
|
||||
}
|
||||
|
||||
void TestWebserver::badRequests()
|
||||
{
|
||||
QFETCH(QByteArray, request);
|
||||
QFETCH(int, expectedStatusCode);
|
||||
|
||||
QTcpSocket *socket = new QTcpSocket(this);
|
||||
socket->connectToHost(QHostAddress("127.0.0.1"), 3000);
|
||||
bool connected = socket->waitForConnected(1000);
|
||||
QVERIFY2(connected, "could not connect to webserver.");
|
||||
|
||||
QSignalSpy clientSpy(socket, SIGNAL(readyRead()));
|
||||
|
||||
socket->write(request);
|
||||
bool filesWritten = socket->waitForBytesWritten(500);
|
||||
QVERIFY2(filesWritten, "could not write to webserver.");
|
||||
|
||||
clientSpy.wait(500);
|
||||
QVERIFY2(clientSpy.count() == 1, "expected exactly 1 response from webserver");
|
||||
QByteArray data = socket->readAll();
|
||||
QVERIFY2(!data.isEmpty(), "got no response");
|
||||
|
||||
QStringList lines = QString(data).split("\r\n");
|
||||
QStringList firstLineTokens = lines.first().split(QRegExp("[ \r\n][ \r\n]*"));
|
||||
|
||||
QVERIFY2(firstLineTokens.isEmpty() || firstLineTokens.count() > 2, "could not get tokens of first line");
|
||||
|
||||
bool ok = false;
|
||||
int statusCode = firstLineTokens.at(1).toInt(&ok);
|
||||
QVERIFY2(ok, "Could not convert statuscode from response to int");
|
||||
QCOMPARE(statusCode, expectedStatusCode);
|
||||
|
||||
socket->close();
|
||||
socket->deleteLater();
|
||||
}
|
||||
|
||||
void TestWebserver::getFiles_data()
|
||||
{
|
||||
QTest::addColumn<QString>("query");
|
||||
@ -164,13 +281,12 @@ void TestWebserver::getFiles()
|
||||
request.setUrl(QUrl("http://localhost:3000" + query));
|
||||
QNetworkReply *reply = nam->get(request);
|
||||
|
||||
clientSpy.wait(200);
|
||||
clientSpy.wait();
|
||||
QVERIFY2(clientSpy.count() == 1, "expected exactly 1 response from webserver");
|
||||
|
||||
printResponse(reply);
|
||||
|
||||
bool ok = false;
|
||||
qDebug() << reply->readAll();
|
||||
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
|
||||
QVERIFY2(ok, "Could not convert statuscode from response to int");
|
||||
QCOMPARE(statusCode, expectedStatusCode);
|
||||
|
||||
Reference in New Issue
Block a user