handle programmatic DS requests except for script creation

This commit is contained in:
Stephen Birarda 2014-01-17 11:29:20 -08:00
parent bce40a9963
commit 40cdba203d
6 changed files with 159 additions and 164 deletions

View file

@ -13,6 +13,7 @@
#include <QtCore/QStringList>
#include <QtCore/QTimer>
#include <HttpConnection.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <UUID.h>
@ -33,7 +34,7 @@ const quint16 DOMAIN_SERVER_HTTP_PORT = 8080;
DomainServer::DomainServer(int argc, char* argv[]) :
QCoreApplication(argc, argv),
_httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath())),
_httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
_assignmentQueueMutex(),
_assignmentQueue(),
_staticAssignmentFile(QString("%1/config.ds").arg(QCoreApplication::applicationDirPath())),
@ -293,142 +294,122 @@ QJsonObject jsonObjectForNode(Node* node) {
return nodeJson;
}
//int DomainServer::civetwebRequestHandler(struct mg_connection *connection) {
// const struct mg_request_info* ri = mg_get_request_info(connection);
//
// const char RESPONSE_200[] = "HTTP/1.0 200 OK\r\n\r\n";
// const char RESPONSE_400[] = "HTTP/1.0 400 Bad Request\r\n\r\n";
//
// const char URI_ASSIGNMENT[] = "/assignment";
// const char URI_NODE[] = "/node";
//
// if (strcmp(ri->request_method, "GET") == 0) {
// if (strcmp(ri->uri, "/assignments.json") == 0) {
// // user is asking for json list of assignments
//
// // start with a 200 response
// mg_printf(connection, "%s", RESPONSE_200);
//
// // setup the JSON
// QJsonObject assignmentJSON;
// QJsonObject assignedNodesJSON;
//
// // enumerate the NodeList to find the assigned nodes
// foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
// if (node->getLinkedData()) {
// // add the node using the UUID as the key
// QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID());
// assignedNodesJSON[uuidString] = jsonObjectForNode(node.data());
// }
// }
//
// assignmentJSON["fulfilled"] = assignedNodesJSON;
//
// QJsonObject queuedAssignmentsJSON;
//
// // add the queued but unfilled assignments to the json
// std::deque<Assignment*>::iterator assignment = domainServerInstance->_assignmentQueue.begin();
//
// while (assignment != domainServerInstance->_assignmentQueue.end()) {
// QJsonObject queuedAssignmentJSON;
//
// QString uuidString = uuidStringWithoutCurlyBraces((*assignment)->getUUID());
// queuedAssignmentJSON[JSON_KEY_TYPE] = QString((*assignment)->getTypeName());
//
// // if the assignment has a pool, add it
// if ((*assignment)->hasPool()) {
// queuedAssignmentJSON[JSON_KEY_POOL] = QString((*assignment)->getPool());
// }
//
// // add this queued assignment to the JSON
// queuedAssignmentsJSON[uuidString] = queuedAssignmentJSON;
//
// // push forward the iterator to check the next assignment
// assignment++;
// }
//
// assignmentJSON["queued"] = queuedAssignmentsJSON;
//
// // print out the created JSON
// QJsonDocument assignmentDocument(assignmentJSON);
// mg_printf(connection, "%s", assignmentDocument.toJson().constData());
//
// // we've processed this request
// return 1;
// } else if (strcmp(ri->uri, "/nodes.json") == 0) {
// // start with a 200 response
// mg_printf(connection, "%s", RESPONSE_200);
//
// // setup the JSON
// QJsonObject rootJSON;
// QJsonObject nodesJSON;
//
// // enumerate the NodeList to find the assigned nodes
// NodeList* nodeList = NodeList::getInstance();
//
// foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
// // add the node using the UUID as the key
// QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID());
// nodesJSON[uuidString] = jsonObjectForNode(node.data());
// }
//
// rootJSON["nodes"] = nodesJSON;
//
// // print out the created JSON
// QJsonDocument nodesDocument(rootJSON);
// mg_printf(connection, "%s", nodesDocument.toJson().constData());
//
// // we've processed this request
// return 1;
// }
//
// // not processed, pass to document root
// return 0;
// } else if (strcmp(ri->request_method, "POST") == 0) {
// if (strcmp(ri->uri, URI_ASSIGNMENT) == 0) {
// // return a 200
// mg_printf(connection, "%s", RESPONSE_200);
// // upload the file
// mg_upload(connection, "/tmp");
//
// return 1;
// }
//
// return 0;
// } else if (strcmp(ri->request_method, "DELETE") == 0) {
// // this is a DELETE request
//
// // check if it is for an assignment
// if (memcmp(ri->uri, URI_NODE, strlen(URI_NODE)) == 0) {
// // pull the UUID from the url
// QUuid deleteUUID = QUuid(QString(ri->uri + strlen(URI_NODE) + sizeof('/')));
//
// if (!deleteUUID.isNull()) {
// SharedNodePointer nodeToKill = NodeList::getInstance()->nodeWithUUID(deleteUUID);
//
// if (nodeToKill) {
// // start with a 200 response
// mg_printf(connection, "%s", RESPONSE_200);
//
// // we have a valid UUID and node - kill the node that has this assignment
// QMetaObject::invokeMethod(NodeList::getInstance(), "killNodeWithUUID", Q_ARG(const QUuid&, deleteUUID));
//
// // successfully processed request
// return 1;
// }
// }
// }
//
// // request not processed - bad request
// mg_printf(connection, "%s", RESPONSE_400);
//
// // this was processed by civetweb
// return 1;
// } else {
// // have mongoose process this request from the document_root
// return 0;
// }
//}
bool DomainServer::handleHTTPRequest(HttpConnection* connection, const QString& path) {
const QString JSON_MIME_TYPE = "application/json";
const QString URI_ASSIGNMENT = "/assignment";
const QString URI_NODE = "/node";
if (connection->requestOperation() == QNetworkAccessManager::GetOperation) {
if (path == "/assignments.json") {
// user is asking for json list of assignments
// setup the JSON
QJsonObject assignmentJSON;
QJsonObject assignedNodesJSON;
// enumerate the NodeList to find the assigned nodes
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getLinkedData()) {
// add the node using the UUID as the key
QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID());
assignedNodesJSON[uuidString] = jsonObjectForNode(node.data());
}
}
assignmentJSON["fulfilled"] = assignedNodesJSON;
QJsonObject queuedAssignmentsJSON;
// add the queued but unfilled assignments to the json
std::deque<Assignment*>::iterator assignment = domainServerInstance->_assignmentQueue.begin();
while (assignment != domainServerInstance->_assignmentQueue.end()) {
QJsonObject queuedAssignmentJSON;
QString uuidString = uuidStringWithoutCurlyBraces((*assignment)->getUUID());
queuedAssignmentJSON[JSON_KEY_TYPE] = QString((*assignment)->getTypeName());
// if the assignment has a pool, add it
if ((*assignment)->hasPool()) {
queuedAssignmentJSON[JSON_KEY_POOL] = QString((*assignment)->getPool());
}
// add this queued assignment to the JSON
queuedAssignmentsJSON[uuidString] = queuedAssignmentJSON;
// push forward the iterator to check the next assignment
assignment++;
}
assignmentJSON["queued"] = queuedAssignmentsJSON;
// print out the created JSON
QJsonDocument assignmentDocument(assignmentJSON);
connection->respond(HttpConnection::StatusCode200, assignmentDocument.toJson(), qPrintable(JSON_MIME_TYPE));
// we've processed this request
return true;
} else if (path == "/nodes.json") {
// setup the JSON
QJsonObject rootJSON;
QJsonObject nodesJSON;
// enumerate the NodeList to find the assigned nodes
NodeList* nodeList = NodeList::getInstance();
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
// add the node using the UUID as the key
QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID());
nodesJSON[uuidString] = jsonObjectForNode(node.data());
}
rootJSON["nodes"] = nodesJSON;
// print out the created JSON
QJsonDocument nodesDocument(rootJSON);
// send the response
connection->respond(HttpConnection::StatusCode200, nodesDocument.toJson(), qPrintable(JSON_MIME_TYPE));
}
} else if (connection->requestOperation() == QNetworkAccessManager::PostOperation) {
if (path == URI_ASSIGNMENT) {
// this is a script upload - ask the HttpConnection to parse the form data
QList<FormData> formData = connection->parseFormData();
qDebug() << formData;
}
} else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) {
if (path.startsWith(URI_NODE)) {
// this is a request to DELETE a node by UUID
// pull the UUID from the url
QUuid deleteUUID = QUuid(path.mid(URI_NODE.size() + sizeof('/')));
if (!deleteUUID.isNull()) {
SharedNodePointer nodeToKill = NodeList::getInstance()->nodeWithUUID(deleteUUID);
if (nodeToKill) {
// start with a 200 response
connection->respond(HttpConnection::StatusCode200);
// we have a valid UUID and node - kill the node that has this assignment
QMetaObject::invokeMethod(NodeList::getInstance(), "killNodeWithUUID", Q_ARG(const QUuid&, deleteUUID));
// successfully processed request
return true;
}
}
// bad request, couldn't pull a node ID
connection->respond(HttpConnection::StatusCode400);
return true;
}
}
// didn't process the request, let the HTTPManager try and handle
return false;
}
const char ASSIGNMENT_SCRIPT_HOST_LOCATION[] = "resources/web/assignment";

View file

@ -21,11 +21,13 @@
const int MAX_STATIC_ASSIGNMENT_FILE_ASSIGNMENTS = 1000;
class DomainServer : public QCoreApplication {
class DomainServer : public QCoreApplication, public HttpRequestHandler {
Q_OBJECT
public:
DomainServer(int argc, char* argv[]);
bool handleHTTPRequest(HttpConnection* connection, const QString& path);
void exit(int retCode = 0);
static void setDomainServerInstance(DomainServer* domainServer);

View file

@ -5,10 +5,6 @@
// Created by Stephen Birarda on 1/16/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// Heavily based on Andrzej Kapolka's original HttpConnection class
// found from another one of his projects.
// (https://github.com/ey6es/witgap/tree/master/src/cpp/server/http)
//
#include <QBuffer>
@ -18,6 +14,10 @@
#include "HttpConnection.h"
#include "HttpManager.h"
const char* HttpConnection::StatusCode200 = "200 OK";
const char* HttpConnection::StatusCode400 = "400 Bad Request";
const char* HttpConnection::StatusCode404 = "404 Not Found";
HttpConnection::HttpConnection (QTcpSocket* socket, HttpManager* parentManager) :
QObject(parentManager),
_parentManager(parentManager),
@ -178,7 +178,7 @@ void HttpConnection::readHeaders() {
QByteArray clength = _requestHeaders.value("Content-Length");
if (clength.isEmpty()) {
_parentManager->handleRequest(this, _requestUrl.path());
_parentManager->handleHTTPRequest(this, _requestUrl.path());
} else {
_requestContent.resize(clength.toInt());
@ -217,5 +217,5 @@ void HttpConnection::readContent() {
_socket->read(_requestContent.data(), size);
_socket->disconnect(this, SLOT(readContent()));
_parentManager->handleRequest(this, _requestUrl.path());
_parentManager->handleHTTPRequest(this, _requestUrl.path());
}

View file

@ -7,7 +7,7 @@
//
// Heavily based on Andrzej Kapolka's original HttpConnection class
// found from another one of his projects.
// (https://github.com/ey6es/witgap/tree/master/src/cpp/server/http)
// https://github.com/ey6es/witgap/tree/master/src/cpp/server/http
//
#ifndef __hifi__HttpConnection__
@ -38,6 +38,9 @@ class HttpConnection : public QObject {
Q_OBJECT
public:
static const char* StatusCode200;
static const char* StatusCode400;
static const char* StatusCode404;
/// WebSocket close status codes.
enum ReasonCode { NoReason = 0, NormalClosure = 1000, GoingAway = 1001 };

View file

@ -5,10 +5,6 @@
// Created by Stephen Birarda on 1/16/14.
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
//
// Heavily based on Andrzej Kapolka's original HttpManager class
// found from another one of his projects.
// (https://github.com/ey6es/witgap/tree/master/src/cpp/server/http)
//
#include <QtCore/QDebug>
#include <QtCore/QFile>
@ -19,7 +15,15 @@
#include "HttpConnection.h"
#include "HttpManager.h"
bool HttpManager::handleRequest(HttpConnection* connection, const QString& path) {
bool HttpManager::handleHTTPRequest(HttpConnection* connection, const QString& path) {
if (_requestHandler && _requestHandler->handleHTTPRequest(connection, path)) {
// this request was handled by our _requestHandler object
// so we don't need to attempt to do so in the document root
return true;
}
// check to see if there is a file to serve from the document root for this path
QString subPath = path;
// remove any slash at the beginning of the path
@ -45,6 +49,8 @@ bool HttpManager::handleRequest(HttpConnection* connection, const QString& path)
if (!filePath.isEmpty()) {
// file exists, serve it
static QMimeDatabase mimeDatabase;
QFile localFile(filePath);
@ -93,18 +99,20 @@ bool HttpManager::handleRequest(HttpConnection* connection, const QString& path)
}
}
connection->respond("200 OK", localFileString.toLocal8Bit(), qPrintable(mimeDatabase.mimeTypeForFile(filePath).name()));
connection->respond(HttpConnection::StatusCode200, localFileString.toLocal8Bit(), qPrintable(mimeDatabase.mimeTypeForFile(filePath).name()));
} else {
// respond with a 404
connection->respond("404 Not Found", "Resource not found.");
connection->respond(HttpConnection::StatusCode404, "Resource not found.");
}
return true;
}
HttpManager::HttpManager(quint16 port, const QString& documentRoot, QObject* parent) :
HttpManager::HttpManager(quint16 port, const QString& documentRoot, HttpRequestHandler* requestHandler, QObject* parent) :
QTcpServer(parent),
_documentRoot(documentRoot) {
_documentRoot(documentRoot),
_requestHandler(requestHandler)
{
// start listening on the passed port
if (!listen(QHostAddress("0.0.0.0"), port)) {
qDebug() << "Failed to open HTTP server socket:" << errorString();

View file

@ -7,36 +7,37 @@
//
// Heavily based on Andrzej Kapolka's original HttpManager class
// found from another one of his projects.
// (https://github.com/ey6es/witgap/tree/master/src/cpp/server/http)
// https://github.com/ey6es/witgap/tree/master/src/cpp/server/http
//
#ifndef __hifi__HttpManager__
#define __hifi__HttpManager__
#include <QByteArray>
#include <QHash>
#include <QtNetwork/QTcpServer>
class HttpConnection;
class HttpRequestHandler;
class HttpRequestHandler {
public:
/// Handles an HTTP request.
virtual bool handleHTTPRequest(HttpConnection* connection, const QString& path) = 0;
};
/// Handles HTTP connections
class HttpManager : public QTcpServer {
class HttpManager : public QTcpServer, public HttpRequestHandler {
Q_OBJECT
public:
/// Initializes the manager.
HttpManager(quint16 port, const QString& documentRoot, QObject* parent = 0);
HttpManager(quint16 port, const QString& documentRoot, HttpRequestHandler* requestHandler = NULL, QObject* parent = 0);
/// Handles an HTTP request.
virtual bool handleRequest (HttpConnection* connection, const QString& path);
bool handleHTTPRequest(HttpConnection* connection, const QString& path);
protected slots:
/// Accepts all pending connections
void acceptConnections();
protected:
QString _documentRoot;
HttpRequestHandler* _requestHandler;
};
#endif /* defined(__hifi__HttpManager__) */