fixed CORS and webserver and tests

This commit is contained in:
Simon Stürz 2015-08-14 14:14:01 +02:00 committed by Michael Zanetti
parent 653043f7b1
commit ba5aedaaab
13 changed files with 78 additions and 89 deletions

View File

@ -79,7 +79,8 @@
}, },
{ {
"name": "Google", "name": "Google",
"id": "d02399c5-8e79-4 0be-8cf6-e00b55bbfe7c", "idName": "google",
"id": "d02399c5-8e79-40be-8cf6-e00b55bbfe7c",
"deviceClasses": [ "deviceClasses": [
{ {
"deviceClassId": "3869884a-1592-4b8f-84a7-994be18ff555", "deviceClassId": "3869884a-1592-4b8f-84a7-994be18ff555",
@ -127,6 +128,7 @@
}, },
{ {
"name": "Yahoo", "name": "Yahoo",
"idName": "yahoo",
"id": "5eb3e1e9-25a7-4740-806e-5cfc836e4bf5", "id": "5eb3e1e9-25a7-4740-806e-5cfc836e4bf5",
"deviceClasses": [ "deviceClasses": [
{ {

View File

@ -125,6 +125,7 @@ HttpReply::HttpReply(QObject *parent) :
m_statusCode(HttpReply::Ok), m_statusCode(HttpReply::Ok),
m_type(HttpReply::TypeSync), m_type(HttpReply::TypeSync),
m_payload(QByteArray()), m_payload(QByteArray()),
m_closeConnection(false),
m_timedOut(false) m_timedOut(false)
{ {
m_timer = new QTimer(this); m_timer = new QTimer(this);
@ -252,6 +253,16 @@ QByteArray HttpReply::rawHeader() const
return m_rawHeader; return m_rawHeader;
} }
void HttpReply::setCloseConnection(const bool &close)
{
m_closeConnection = close;
}
bool HttpReply::closeConnection() const
{
return m_closeConnection;
}
/*! Returns true if the raw header and the payload of this \l{HttpReply} is empty.*/ /*! Returns true if the raw header and the payload of this \l{HttpReply} is empty.*/
bool HttpReply::isEmpty() const bool HttpReply::isEmpty() const
{ {

View File

@ -94,6 +94,9 @@ public:
QHash<QByteArray, QByteArray> rawHeaderList() const; QHash<QByteArray, QByteArray> rawHeaderList() const;
QByteArray rawHeader() const; QByteArray rawHeader() const;
void setCloseConnection(const bool &close);
bool closeConnection() const;
bool isEmpty() const; bool isEmpty() const;
void clear(); void clear();
@ -115,6 +118,8 @@ private:
QHash<QByteArray, QByteArray> m_rawHeaderList; QHash<QByteArray, QByteArray> m_rawHeaderList;
bool m_closeConnection;
QTimer *m_timer; QTimer *m_timer;
bool m_timedOut; bool m_timedOut;

View File

@ -63,10 +63,10 @@ int main(int argc, char *argv[])
s_loggingFilters.insert("RuleEngine", true); s_loggingFilters.insert("RuleEngine", true);
s_loggingFilters.insert("Connection", true); s_loggingFilters.insert("Connection", true);
s_loggingFilters.insert("TcpServer", false); s_loggingFilters.insert("TcpServer", false);
s_loggingFilters.insert("WebServer", false); s_loggingFilters.insert("WebServer", true);
s_loggingFilters.insert("WebSocketServer", false); s_loggingFilters.insert("WebSocketServer", false);
s_loggingFilters.insert("JsonRpc", false); s_loggingFilters.insert("JsonRpc", false);
s_loggingFilters.insert("Rest", false); s_loggingFilters.insert("Rest", true);
s_loggingFilters.insert("Hardware", false); s_loggingFilters.insert("Hardware", false);
s_loggingFilters.insert("LogEngine", false); s_loggingFilters.insert("LogEngine", false);

View File

@ -51,6 +51,7 @@ HttpReply *DeviceClassesResource::proccessRequest(const HttpRequest &request, co
} }
m_deviceClass = GuhCore::instance()->findDeviceClass(deviceClassId); m_deviceClass = GuhCore::instance()->findDeviceClass(deviceClassId);
if (!m_deviceClass.isValid()) { if (!m_deviceClass.isValid()) {
qCWarning(dcRest) << "DeviceClassId" << deviceClassId.toString() << "not found";
return createErrorReply(HttpReply::NotFound); return createErrorReply(HttpReply::NotFound);
} }
} }

View File

@ -250,9 +250,11 @@ HttpReply *DevicesResource::removeDevice(Device *device) const
// TODO: /api/v1/devices/{deviceId}?ruleId={ruleId}&removePolicy={RemovePolicy} // TODO: /api/v1/devices/{deviceId}?ruleId={ruleId}&removePolicy={RemovePolicy}
if (result == DeviceManager::DeviceErrorNoError) if (result == DeviceManager::DeviceErrorNoError) {
return createSuccessReply(); HttpReply *reply = createSuccessReply();
reply->setCloseConnection(true);
return reply;
}
return createErrorReply(HttpReply::Forbidden); return createErrorReply(HttpReply::Forbidden);
} }
@ -322,7 +324,7 @@ HttpReply *DevicesResource::addConfiguredDevice(const QByteArray &payload) const
return createErrorReply(HttpReply::InternalServerError); return createErrorReply(HttpReply::InternalServerError);
QVariantMap result; QVariantMap result;
result.insert("deviceId", newDeviceId); result.insert("id", newDeviceId);
HttpReply *reply = createSuccessReply(); HttpReply *reply = createSuccessReply();
reply->setPayload(QJsonDocument::fromVariant(result).toJson()); reply->setPayload(QJsonDocument::fromVariant(result).toJson());
return reply; return reply;
@ -458,7 +460,7 @@ void DevicesResource::deviceSetupFinished(Device *device, DeviceManager::DeviceE
} }
QVariantMap result; QVariantMap result;
result.insert("deviceId", device->id()); result.insert("id", device->id());
reply->setPayload(QJsonDocument::fromVariant(result).toJson()); reply->setPayload(QJsonDocument::fromVariant(result).toJson());
reply->finished(); reply->finished();
} }
@ -489,7 +491,7 @@ void DevicesResource::pairingFinished(const PairingTransactionId &pairingTransac
if (status == DeviceManager::DeviceErrorNoError) { if (status == DeviceManager::DeviceErrorNoError) {
qCDebug(dcRest) << "Pairing device finished successfully"; qCDebug(dcRest) << "Pairing device finished successfully";
QVariantMap response; QVariantMap response;
response.insert("deviceId", deviceId.toString()); response.insert("id", deviceId.toString());
reply->setPayload(QJsonDocument::fromVariant(response).toJson()); reply->setPayload(QJsonDocument::fromVariant(response).toJson());
reply->setHttpStatusCode(HttpReply::Ok); reply->setHttpStatusCode(HttpReply::Ok);
} else { } else {

View File

@ -111,6 +111,21 @@ void RestServer::processHttpRequest(const QUuid &clientId, const HttpRequest &re
return; return;
} }
// check CORS call
if (request.method() == HttpRequest::Options) {
HttpReply *reply = RestResource::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain;");
reply->setRawHeader("Access-Control-Allow-Methods", "PUT, POST, GET, DELETE, OPTIONS");
reply->setRawHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
reply->setClientId(clientId);
reply->setCloseConnection(true);
m_webserver->sendHttpReply(reply);
reply->deleteLater();
return;
}
// process request in corresponding resource // process request in corresponding resource
RestResource *resource = m_resources.value(resourceName); RestResource *resource = m_resources.value(resourceName);
HttpReply *reply = resource->proccessRequest(request, urlTokens); HttpReply *reply = resource->proccessRequest(request, urlTokens);

View File

@ -234,7 +234,7 @@ HttpReply *RulesResource::addRule(const QByteArray &payload) const
if (status == RuleEngine::RuleErrorNoError) { if (status == RuleEngine::RuleErrorNoError) {
QVariantMap returns; QVariantMap returns;
returns.insert("ruleId", newRuleId.toString()); returns.insert("id", newRuleId.toString());
HttpReply *reply = createSuccessReply(); HttpReply *reply = createSuccessReply();
reply->setPayload(QJsonDocument::fromVariant(returns).toJson()); reply->setPayload(QJsonDocument::fromVariant(returns).toJson());
return reply; return reply;

View File

@ -133,10 +133,12 @@ void WebServer::sendHttpReply(HttpReply *reply)
QSslSocket *socket = 0; QSslSocket *socket = 0;
socket = m_clientList.value(reply->clientId()); socket = m_clientList.value(reply->clientId());
if (!socket) { if (!socket) {
qCDebug(dcWebServer) << "Invalid socket pointer! This should never happen!!!"; qCDebug(dcWebServer) << "Invalid socket pointer! This should never happen!!! Missing clientId in reply?";
return; return;
} }
writeData(socket, reply->data()); writeData(socket, reply->data());
socket->close();
} }
bool WebServer::verifyFile(QSslSocket *socket, const QString &fileName) bool WebServer::verifyFile(QSslSocket *socket, const QString &fileName)
@ -190,7 +192,6 @@ QString WebServer::fileName(const QString &query)
void WebServer::writeData(QSslSocket *socket, const QByteArray &data) void WebServer::writeData(QSslSocket *socket, const QByteArray &data)
{ {
socket->write(data); socket->write(data);
//socket->close();
} }
void WebServer::incomingConnection(qintptr socketDescriptor) void WebServer::incomingConnection(qintptr socketDescriptor)
@ -291,13 +292,6 @@ void WebServer::readClient()
return; return;
} }
// check CORS call
if (request.method() == HttpRequest::Options) {
HttpReply reply(HttpReply::Ok);
reply.setRawHeader("Access-Control-Allow-Methods","PUT, POST, GET, DELETE, OPTIONS");
writeData(socket, reply.data());
}
// verify API query // verify API query
if (request.url().path().startsWith("/api/v1")) { if (request.url().path().startsWith("/api/v1")) {
emit httpRequestReady(clientId, request); emit httpRequestReady(clientId, request);

View File

@ -93,8 +93,8 @@ void TestRestDeviceClasses::getSupportedDevices()
QNetworkReply *reply = nam->get(request); QNetworkReply *reply = nam->get(request);
clientSpy.wait(); clientSpy.wait();
QVERIFY2(clientSpy.count() == 1, "expected exactly 1 response from webserver"); QVERIFY2(clientSpy.count() == 1, "expected exactly 1 response from webserver");
// jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error); jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error);
// QCOMPARE(error.error, QJsonParseError::NoError); QCOMPARE(error.error, QJsonParseError::NoError);
reply->deleteLater(); reply->deleteLater();
} }
nam->deleteLater(); nam->deleteLater();
@ -276,8 +276,11 @@ void TestRestDeviceClasses::discoverDevices()
QVariantList foundDevices = jsonDoc.toVariant().toList(); QVariantList foundDevices = jsonDoc.toVariant().toList();
QCOMPARE(foundDevices.count(), resultCount); QCOMPARE(foundDevices.count(), resultCount);
qDebug() << jsonDoc.toJson();
// ADD the discovered device // ADD the discovered device
request.setUrl(QUrl("http://localhost:3333/api/v1/devices")); request.setUrl(QUrl("http://localhost:3333/api/v1/devices"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
DeviceDescriptorId descriptorId = DeviceDescriptorId(foundDevices.first().toMap().value("id").toString()); DeviceDescriptorId descriptorId = DeviceDescriptorId(foundDevices.first().toMap().value("id").toString());
qDebug() << descriptorId; qDebug() << descriptorId;
params.clear(); params.clear();
@ -286,21 +289,25 @@ void TestRestDeviceClasses::discoverDevices()
clientSpy.clear(); clientSpy.clear();
QByteArray payload = QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact); QByteArray payload = QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact);
qDebug() << payload;
reply = nam->post(request, payload); reply = nam->post(request, payload);
clientSpy.wait(); clientSpy.wait();
QCOMPARE(clientSpy.count(), 1); QCOMPARE(clientSpy.count(), 1);
data = reply->readAll(); data = reply->readAll();
qDebug() << reply->rawHeaderList(); qDebug() << data;
statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
reply->deleteLater(); reply->deleteLater();
QCOMPARE(statusCode, expectedStatusCode); QCOMPARE(statusCode, expectedStatusCode);
// REMOVE added device
jsonDoc = QJsonDocument::fromJson(data, &error); jsonDoc = QJsonDocument::fromJson(data, &error);
QCOMPARE(error.error, QJsonParseError::NoError); QCOMPARE(error.error, QJsonParseError::NoError);
QVariantMap response = jsonDoc.toVariant().toMap(); QVariantMap response = jsonDoc.toVariant().toMap();
DeviceId deviceId = DeviceId(response.value("deviceId").toString()); DeviceId deviceId = DeviceId(response.value("id").toString());
QVERIFY2(!deviceId.isNull(), "got invalid device id");
// REMOVE added device
request = QNetworkRequest(QUrl(QString("http://localhost:3333/api/v1/devices/%1").arg(deviceId.toString()))); request = QNetworkRequest(QUrl(QString("http://localhost:3333/api/v1/devices/%1").arg(deviceId.toString())));
clientSpy.clear(); clientSpy.clear();

View File

@ -202,7 +202,8 @@ void TestRestDevices::addConfiguredDevice()
QCOMPARE(error.error, QJsonParseError::NoError); QCOMPARE(error.error, QJsonParseError::NoError);
QVariantMap response = jsonDoc.toVariant().toMap(); QVariantMap response = jsonDoc.toVariant().toMap();
DeviceId deviceId = DeviceId(response.value("deviceId").toString()); DeviceId deviceId = DeviceId(response.value("id").toString());
QVERIFY2(!deviceId.isNull(),"invalid device id for removing");
request.setUrl(QUrl(QString("http://localhost:3333/api/v1/devices/%1").arg(deviceId.toString()))); request.setUrl(QUrl(QString("http://localhost:3333/api/v1/devices/%1").arg(deviceId.toString())));
clientSpy.clear(); clientSpy.clear();
@ -430,7 +431,7 @@ void TestRestDevices::editDevices()
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
reply->deleteLater(); reply->deleteLater();
QVariantMap responseMap = QJsonDocument::fromJson(data).toVariant().toMap(); QVariantMap responseMap = QJsonDocument::fromJson(data).toVariant().toMap();
DeviceId deviceId = DeviceId(responseMap.value("deviceId").toString()); DeviceId deviceId = DeviceId(responseMap.value("id").toString());
qDebug() << deviceId.toString(); qDebug() << deviceId.toString();
QVERIFY2(deviceId != DeviceId(), "DeviceId not returned"); QVERIFY2(deviceId != DeviceId(), "DeviceId not returned");
@ -561,7 +562,7 @@ void TestRestDevices::editByDiscovery()
QCOMPARE(error.error, QJsonParseError::NoError); QCOMPARE(error.error, QJsonParseError::NoError);
QVariantMap response = jsonDoc.toVariant().toMap(); QVariantMap response = jsonDoc.toVariant().toMap();
DeviceId deviceId = DeviceId(response.value("deviceId").toString()); DeviceId deviceId = DeviceId(response.value("id").toString());
QVERIFY(!deviceId.isNull()); QVERIFY(!deviceId.isNull());
@ -618,62 +619,6 @@ void TestRestDevices::editByDiscovery()
QCOMPARE(statusCode, expectedStatusCode); QCOMPARE(statusCode, expectedStatusCode);
reply->deleteLater(); reply->deleteLater();
// response = injectAndWait("Devices.EditDevice", params);
// verifyDeviceError(response, error);
// response.clear();
// response = injectAndWait("Devices.GetConfiguredDevices", QVariantMap());
// QVariantMap deviceMap;
// bool found = false;
// foreach (const QVariant device, response.toMap().value("params").toMap().value("devices").toList()) {
// if (DeviceId(device.toMap().value("id").toString()) == deviceId) {
// qDebug() << "found added device" << device.toMap().value("params");
// found = true;
// deviceMap = device.toMap();
// break;
// }
// }
// QVERIFY2(found, "Device missing in config!");
// QCOMPARE(deviceMap.value("id").toString(), deviceId.toString());
// if (deviceMap.contains("setupComplete")) {
// QVERIFY2(deviceMap.value("setupComplete").toBool(), "Setup not completed after edit");
// }
// // Note: this shows that by discovery a not editable param (name) can be changed!
// foreach (QVariant param, deviceMap.value("params").toList()) {
// if (param.toMap().value("name") == "name") {
// QCOMPARE(param.toMap().value("value").toString(), QString("Discovered Mock Device 2"));
// }
// if (param.toMap().value("name") == "httpport") {
// QCOMPARE(param.toMap().value("value").toInt(), 55556);
// }
// }
// // check if the daemons are running
// QNetworkAccessManager nam;
// QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
// // check if old daemon is still running (should not)
// QNetworkRequest request(QUrl(QString("http://localhost:%1").arg(55555)));
// QNetworkReply *reply = nam.get(request);
// spy.wait();
// QVERIFY2(reply->error(), "The old daemon is still running");
// reply->deleteLater();
// // check if the daemon is realy running on the new port
// request = QNetworkRequest(QUrl(QString("http://localhost:%1").arg(55556)));
// reply = nam.get(request);
// spy.wait();
// QVERIFY2(reply->error() == QNetworkReply::NoError, "The new daemon is not running");
// reply->deleteLater();
// remove added device // remove added device
request = QNetworkRequest(QUrl(QString("http://localhost:3333/api/v1/devices/%1").arg(deviceId.toString()))); request = QNetworkRequest(QUrl(QString("http://localhost:3333/api/v1/devices/%1").arg(deviceId.toString())));
clientSpy.clear(); clientSpy.clear();

View File

@ -363,7 +363,7 @@ void TestRestRules::addRemoveRules()
QCOMPARE(error.error, QJsonParseError::NoError); QCOMPARE(error.error, QJsonParseError::NoError);
reply->deleteLater(); reply->deleteLater();
RuleId ruleId = RuleId(jsonDoc.toVariant().toMap().value("ruleId").toString()); RuleId ruleId = RuleId(jsonDoc.toVariant().toMap().value("id").toString());
QVERIFY(!ruleId.isNull()); QVERIFY(!ruleId.isNull());
// GET rule details // GET rule details
@ -677,7 +677,7 @@ void TestRestRules::editRules()
QCOMPARE(error.error, QJsonParseError::NoError); QCOMPARE(error.error, QJsonParseError::NoError);
reply->deleteLater(); reply->deleteLater();
RuleId ruleId = RuleId(jsonDoc.toVariant().toMap().value("ruleId").toString()); RuleId ruleId = RuleId(jsonDoc.toVariant().toMap().value("id").toString());
QVERIFY(!ruleId.isNull()); QVERIFY(!ruleId.isNull());
// now create the new rule and edit the original one // now create the new rule and edit the original one
@ -792,7 +792,7 @@ void TestRestRules::enableDisableRule()
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error); QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
QCOMPARE(error.error, QJsonParseError::NoError); QCOMPARE(error.error, QJsonParseError::NoError);
RuleId ruleId = RuleId(jsonDoc.toVariant().toMap().value("ruleId").toString()); RuleId ruleId = RuleId(jsonDoc.toVariant().toMap().value("id").toString());
QVERIFY(!ruleId.isNull()); QVERIFY(!ruleId.isNull());
// ENABLE rule // ENABLE rule

View File

@ -176,6 +176,8 @@ void TestWebserver::checkAllowedMethodCall()
request.setUrl(QUrl("http://localhost:3333")); request.setUrl(QUrl("http://localhost:3333"));
QNetworkReply *reply = 0; QNetworkReply *reply = 0;
clientSpy.clear();
if (method == "GET") { if (method == "GET") {
reply = nam->get(request); reply = nam->get(request);
} else if(method == "PUT") { } else if(method == "PUT") {
@ -189,6 +191,7 @@ void TestWebserver::checkAllowedMethodCall()
} else if(method == "CONNECT") { } else if(method == "CONNECT") {
reply = nam->sendCustomRequest(request, "CONNECT"); reply = nam->sendCustomRequest(request, "CONNECT");
} else if(method == "OPTIONS") { } else if(method == "OPTIONS") {
request.setUrl(QUrl("http://localhost:3333/api/v1/devices"));
reply = nam->sendCustomRequest(request, "OPTIONS"); reply = nam->sendCustomRequest(request, "OPTIONS");
} else if(method == "TRACE") { } else if(method == "TRACE") {
reply = nam->sendCustomRequest(request, "TRACE"); reply = nam->sendCustomRequest(request, "TRACE");
@ -197,8 +200,13 @@ void TestWebserver::checkAllowedMethodCall()
reply = nam->get(request); reply = nam->get(request);
} }
clientSpy.wait(200); clientSpy.wait();
QVERIFY2(clientSpy.count() == 1, "expected exactly 1 response from webserver");
printResponse(reply);
QCOMPARE(clientSpy.count(), 1);
//QVERIFY2(clientSpy.count() == 1, "expected exactly 1 response from webserver");
if (expectedStatusCode == 405){ if (expectedStatusCode == 405){
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), expectedStatusCode); QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), expectedStatusCode);
QVERIFY2(reply->hasRawHeader("Allow"), "405 should contain the allowed methods header"); QVERIFY2(reply->hasRawHeader("Allow"), "405 should contain the allowed methods header");
@ -229,7 +237,6 @@ void TestWebserver::badRequests_data()
userAgentMissing.append("GET / HTTP/1.1\r\n"); userAgentMissing.append("GET / HTTP/1.1\r\n");
userAgentMissing.append("\r\n"); userAgentMissing.append("\r\n");
QTest::newRow("wrong content length") << wrongContentLength << 400; QTest::newRow("wrong content length") << wrongContentLength << 400;
QTest::newRow("invalid header formatting") << wrongHeaderFormatting << 400; QTest::newRow("invalid header formatting") << wrongHeaderFormatting << 400;
QTest::newRow("user agent missing") << userAgentMissing << 400; QTest::newRow("user agent missing") << userAgentMissing << 400;