mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 06:38:29 +02:00
setup the HTTPManager to serve files in the document root
This commit is contained in:
parent
c5e10465c4
commit
036dba9c2f
5 changed files with 62 additions and 436 deletions
|
@ -33,7 +33,7 @@ const quint16 DOMAIN_SERVER_HTTP_PORT = 8080;
|
||||||
|
|
||||||
DomainServer::DomainServer(int argc, char* argv[]) :
|
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
QCoreApplication(argc, argv),
|
QCoreApplication(argc, argv),
|
||||||
_httpManager(DOMAIN_SERVER_HTTP_PORT),
|
_httpManager(DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath())),
|
||||||
_assignmentQueueMutex(),
|
_assignmentQueueMutex(),
|
||||||
_assignmentQueue(),
|
_assignmentQueue(),
|
||||||
_staticAssignmentFile(QString("%1/config.ds").arg(QCoreApplication::applicationDirPath())),
|
_staticAssignmentFile(QString("%1/config.ds").arg(QCoreApplication::applicationDirPath())),
|
||||||
|
@ -60,8 +60,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
||||||
|
|
||||||
const char METAVOXEL_CONFIG_OPTION[] = "--metavoxelServerConfig";
|
const char METAVOXEL_CONFIG_OPTION[] = "--metavoxelServerConfig";
|
||||||
_metavoxelServerConfig = getCmdOption(argc, (const char**)argv, METAVOXEL_CONFIG_OPTION);
|
_metavoxelServerConfig = getCmdOption(argc, (const char**)argv, METAVOXEL_CONFIG_OPTION);
|
||||||
|
|
||||||
// QString documentRootString = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath());
|
|
||||||
//
|
//
|
||||||
// char* documentRoot = new char[documentRootString.size() + 1];
|
// char* documentRoot = new char[documentRootString.size() + 1];
|
||||||
// strcpy(documentRoot, documentRootString.toLocal8Bit().constData());
|
// strcpy(documentRoot, documentRootString.toLocal8Bit().constData());
|
||||||
|
|
|
@ -22,11 +22,8 @@ HttpConnection::HttpConnection (QTcpSocket* socket, HttpManager* parentManager)
|
||||||
QObject(parentManager),
|
QObject(parentManager),
|
||||||
_parentManager(parentManager),
|
_parentManager(parentManager),
|
||||||
_socket(socket),
|
_socket(socket),
|
||||||
_unmasker(new MaskFilter(socket, this)),
|
|
||||||
_stream(socket),
|
_stream(socket),
|
||||||
_address(socket->peerAddress()),
|
_address(socket->peerAddress())
|
||||||
_webSocketPaused(false),
|
|
||||||
_closeSent(false)
|
|
||||||
{
|
{
|
||||||
// take over ownership of the socket
|
// take over ownership of the socket
|
||||||
_socket->setParent(this);
|
_socket->setParent(this);
|
||||||
|
@ -50,11 +47,6 @@ HttpConnection::~HttpConnection ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpConnection::isWebSocketRequest ()
|
|
||||||
{
|
|
||||||
return _requestHeaders.value("Upgrade") == "websocket";
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<FormData> HttpConnection::parseFormData () const
|
QList<FormData> HttpConnection::parseFormData () const
|
||||||
{
|
{
|
||||||
// make sure we have the correct MIME type
|
// make sure we have the correct MIME type
|
||||||
|
@ -111,9 +103,7 @@ QList<FormData> HttpConnection::parseFormData () const
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpConnection::respond (
|
void HttpConnection::respond (const char* code, const QByteArray& content, const char* contentType, const Headers& headers) {
|
||||||
const char* code, const QByteArray& content, const char* contentType, const Headers& headers)
|
|
||||||
{
|
|
||||||
_socket->write("HTTP/1.1 ");
|
_socket->write("HTTP/1.1 ");
|
||||||
_socket->write(code);
|
_socket->write(code);
|
||||||
_socket->write("\r\n");
|
_socket->write("\r\n");
|
||||||
|
@ -139,6 +129,7 @@ void HttpConnection::respond (
|
||||||
_socket->write("Connection: close\r\n\r\n");
|
_socket->write("Connection: close\r\n\r\n");
|
||||||
|
|
||||||
if (csize > 0) {
|
if (csize > 0) {
|
||||||
|
qDebug() << "Writing" << QString(content) << "\n";
|
||||||
_socket->write(content);
|
_socket->write(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,55 +139,6 @@ void HttpConnection::respond (
|
||||||
_socket->disconnectFromHost();
|
_socket->disconnectFromHost();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpConnection::switchToWebSocket (const char* protocol)
|
|
||||||
{
|
|
||||||
_socket->write("HTTP/1.1 101 Switching Protocols\r\n");
|
|
||||||
_socket->write("Upgrade: websocket\r\n");
|
|
||||||
_socket->write("Connection: Upgrade\r\n");
|
|
||||||
_socket->write("Sec-WebSocket-Accept: ");
|
|
||||||
|
|
||||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
|
||||||
hash.addData(_requestHeaders.value("Sec-WebSocket-Key"));
|
|
||||||
hash.addData("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); // from WebSocket draft RFC
|
|
||||||
_socket->write(hash.result().toBase64());
|
|
||||||
|
|
||||||
if (protocol != 0) {
|
|
||||||
_socket->write("\r\nSec-WebSocket-Protocol: ");
|
|
||||||
_socket->write(protocol);
|
|
||||||
}
|
|
||||||
_socket->write("\r\n\r\n");
|
|
||||||
|
|
||||||
// connect socket, start reading frames
|
|
||||||
setWebSocketPaused(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpConnection::setWebSocketPaused (bool paused)
|
|
||||||
{
|
|
||||||
if ((_webSocketPaused = paused)) {
|
|
||||||
_socket->disconnect(this, SLOT(readFrames()));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
connect(_socket, SIGNAL(readyRead()), SLOT(readFrames()));
|
|
||||||
readFrames();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpConnection::closeWebSocket (quint16 reasonCode, const char* reason)
|
|
||||||
{
|
|
||||||
if (reasonCode == NoReason) {
|
|
||||||
writeFrameHeader(ConnectionClose);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
int rlen = (reason == 0) ? 0 : qstrlen(reason);
|
|
||||||
writeFrameHeader(ConnectionClose, 2 + rlen);
|
|
||||||
_stream << reasonCode;
|
|
||||||
if (rlen > 0) {
|
|
||||||
_socket->write(reason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_closeSent = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpConnection::readRequest ()
|
void HttpConnection::readRequest ()
|
||||||
{
|
{
|
||||||
if (!_socket->canReadLine()) {
|
if (!_socket->canReadLine()) {
|
||||||
|
@ -245,7 +187,7 @@ void HttpConnection::readHeaders ()
|
||||||
|
|
||||||
QByteArray clength = _requestHeaders.value("Content-Length");
|
QByteArray clength = _requestHeaders.value("Content-Length");
|
||||||
if (clength.isEmpty()) {
|
if (clength.isEmpty()) {
|
||||||
_parentManager->handleRequest(this, "", _requestUrl.path());
|
_parentManager->handleRequest(this, _requestUrl.path());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
_requestContent.resize(clength.toInt());
|
_requestContent.resize(clength.toInt());
|
||||||
|
@ -276,8 +218,7 @@ void HttpConnection::readHeaders ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpConnection::readContent ()
|
void HttpConnection::readContent() {
|
||||||
{
|
|
||||||
int size = _requestContent.size();
|
int size = _requestContent.size();
|
||||||
if (_socket->bytesAvailable() < size) {
|
if (_socket->bytesAvailable() < size) {
|
||||||
return;
|
return;
|
||||||
|
@ -285,201 +226,5 @@ void HttpConnection::readContent ()
|
||||||
_socket->read(_requestContent.data(), size);
|
_socket->read(_requestContent.data(), size);
|
||||||
_socket->disconnect(this, SLOT(readContent()));
|
_socket->disconnect(this, SLOT(readContent()));
|
||||||
|
|
||||||
_parentManager->handleRequest(this, "", _requestUrl.path());
|
_parentManager->handleRequest(this, _requestUrl.path());
|
||||||
}
|
|
||||||
|
|
||||||
void HttpConnection::readFrames()
|
|
||||||
{
|
|
||||||
// read as many messages as are available
|
|
||||||
while (maybeReadFrame());
|
|
||||||
}
|
|
||||||
|
|
||||||
void unget (QIODevice* device, quint32 value) {
|
|
||||||
device->ungetChar(value & 0xFF);
|
|
||||||
device->ungetChar((value >> 8) & 0xFF);
|
|
||||||
device->ungetChar((value >> 16) & 0xFF);
|
|
||||||
device->ungetChar(value >> 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HttpConnection::maybeReadFrame ()
|
|
||||||
{
|
|
||||||
// make sure we have at least the first two bytes
|
|
||||||
qint64 available = _socket->bytesAvailable();
|
|
||||||
if (available < 2 || _webSocketPaused) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// read the first two, which tell us whether we need more for the length
|
|
||||||
quint8 finalOpcode, maskLength;
|
|
||||||
_stream >> finalOpcode;
|
|
||||||
_stream >> maskLength;
|
|
||||||
available -= 2;
|
|
||||||
|
|
||||||
int byteLength = maskLength & 0x7F;
|
|
||||||
bool masked = (maskLength & 0x80) != 0;
|
|
||||||
int baseLength = (masked ? 4 : 0);
|
|
||||||
int length = -1;
|
|
||||||
if (byteLength == 127) {
|
|
||||||
if (available >= 8) {
|
|
||||||
quint64 longLength;
|
|
||||||
_stream >> longLength;
|
|
||||||
if (available >= baseLength + 8 + longLength) {
|
|
||||||
length = longLength;
|
|
||||||
} else {
|
|
||||||
unget(_socket, longLength & 0xFFFFFFFF);
|
|
||||||
unget(_socket, longLength >> 32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (byteLength == 126) {
|
|
||||||
if (available >= 2) {
|
|
||||||
quint16 shortLength;
|
|
||||||
_stream >> shortLength;
|
|
||||||
if (available >= baseLength + 2 + shortLength) {
|
|
||||||
length = shortLength;
|
|
||||||
} else {
|
|
||||||
_socket->ungetChar(shortLength & 0xFF);
|
|
||||||
_socket->ungetChar(shortLength >> 8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (available >= baseLength + byteLength) {
|
|
||||||
length = byteLength;
|
|
||||||
}
|
|
||||||
if (length == -1) {
|
|
||||||
_socket->ungetChar(maskLength);
|
|
||||||
_socket->ungetChar(finalOpcode);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read the mask and set it in the filter
|
|
||||||
quint32 mask = 0;
|
|
||||||
if (masked) {
|
|
||||||
_stream >> mask;
|
|
||||||
}
|
|
||||||
_unmasker->setMask(mask);
|
|
||||||
|
|
||||||
// if not final, add to continuing message
|
|
||||||
FrameOpcode opcode = (FrameOpcode)(finalOpcode & 0x0F);
|
|
||||||
if ((finalOpcode & 0x80) == 0) {
|
|
||||||
if (opcode != ContinuationFrame) {
|
|
||||||
_continuingOpcode = opcode;
|
|
||||||
}
|
|
||||||
_continuingMessage += _unmasker->read(length);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if continuing, add to and read from buffer
|
|
||||||
QIODevice* device = _unmasker;
|
|
||||||
FrameOpcode copcode = opcode;
|
|
||||||
if (opcode == ContinuationFrame) {
|
|
||||||
_continuingMessage += _unmasker->read(length);
|
|
||||||
device = new QBuffer(&_continuingMessage, this);
|
|
||||||
device->open(QIODevice::ReadOnly);
|
|
||||||
copcode = _continuingOpcode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// act according to opcode
|
|
||||||
switch (copcode) {
|
|
||||||
case TextFrame:
|
|
||||||
emit webSocketMessageAvailable(device, length, true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BinaryFrame:
|
|
||||||
emit webSocketMessageAvailable(device, length, false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ConnectionClose:
|
|
||||||
// if this is not a response to our own close request, send a close reply
|
|
||||||
if (!_closeSent) {
|
|
||||||
closeWebSocket(GoingAway);
|
|
||||||
}
|
|
||||||
if (length >= 2) {
|
|
||||||
QDataStream stream(device);
|
|
||||||
quint16 reasonCode;
|
|
||||||
stream >> reasonCode;
|
|
||||||
emit webSocketClosed(reasonCode, device->read(length - 2));
|
|
||||||
} else {
|
|
||||||
emit webSocketClosed(0, QByteArray());
|
|
||||||
}
|
|
||||||
_socket->disconnectFromHost();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Ping:
|
|
||||||
// send the pong out immediately
|
|
||||||
writeFrameHeader(Pong, length, true);
|
|
||||||
_socket->write(device->read(length));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Pong:
|
|
||||||
qWarning() << "Got unsolicited WebSocket pong." << _address << device->read(length);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
qWarning() << "Received unknown WebSocket opcode." << _address << opcode <<
|
|
||||||
device->read(length);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear the continuing message buffer
|
|
||||||
if (opcode == ContinuationFrame) {
|
|
||||||
_continuingMessage.clear();
|
|
||||||
delete device;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HttpConnection::writeFrameHeader (FrameOpcode opcode, int size, bool final)
|
|
||||||
{
|
|
||||||
if (_closeSent) {
|
|
||||||
qWarning() << "Writing frame header after close message." << _address << opcode;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_socket->putChar((final ? 0x80 : 0x0) | opcode);
|
|
||||||
if (size < 126) {
|
|
||||||
_socket->putChar(size);
|
|
||||||
|
|
||||||
} else if (size < 65536) {
|
|
||||||
_socket->putChar(126);
|
|
||||||
_stream << (quint16)size;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
_socket->putChar(127);
|
|
||||||
_stream << (quint64)size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MaskFilter::MaskFilter (QIODevice* device, QObject* parent) :
|
|
||||||
QIODevice(parent),
|
|
||||||
_device(device)
|
|
||||||
{
|
|
||||||
open(ReadOnly);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MaskFilter::setMask (quint32 mask)
|
|
||||||
{
|
|
||||||
_mask[0] = (mask >> 24);
|
|
||||||
_mask[1] = (mask >> 16) & 0xFF;
|
|
||||||
_mask[2] = (mask >> 8) & 0xFF;
|
|
||||||
_mask[3] = mask & 0xFF;
|
|
||||||
_position = 0;
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
qint64 MaskFilter::bytesAvailable () const
|
|
||||||
{
|
|
||||||
return _device->bytesAvailable() + QIODevice::bytesAvailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
qint64 MaskFilter::readData (char* data, qint64 maxSize)
|
|
||||||
{
|
|
||||||
qint64 bytes = _device->read(data, maxSize);
|
|
||||||
for (char* end = data + bytes; data < end; data++) {
|
|
||||||
*data ^= _mask[_position];
|
|
||||||
_position = (_position + 1) % 4;
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
qint64 MaskFilter::writeData (const char* data, qint64 maxSize)
|
|
||||||
{
|
|
||||||
return _device->write(data, maxSize);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,11 +81,6 @@ public:
|
||||||
*/
|
*/
|
||||||
const QByteArray& requestContent () const { return _requestContent; }
|
const QByteArray& requestContent () const { return _requestContent; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the request is asking to switch to a WebSocket.
|
|
||||||
*/
|
|
||||||
bool isWebSocketRequest ();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the request content as form data, returning a list of header/content pairs.
|
* Parses the request content as form data, returning a list of header/content pairs.
|
||||||
*/
|
*/
|
||||||
|
@ -97,28 +92,6 @@ public:
|
||||||
void respond (const char* code, const QByteArray& content = QByteArray(),
|
void respond (const char* code, const QByteArray& content = QByteArray(),
|
||||||
const char* contentType = "text/plain; charset=ISO-8859-1",
|
const char* contentType = "text/plain; charset=ISO-8859-1",
|
||||||
const Headers& headers = Headers());
|
const Headers& headers = Headers());
|
||||||
|
|
||||||
/**
|
|
||||||
* Switches to a WebSocket.
|
|
||||||
*/
|
|
||||||
void switchToWebSocket (const char* protocol = 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a header for a WebSocket message of the specified size. The body of the message
|
|
||||||
* should be written through the socket.
|
|
||||||
*/
|
|
||||||
void writeWebSocketHeader (int size) { writeFrameHeader(BinaryFrame, size); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pauses or unpauses the WebSocket. A paused WebSocket buffers messages until unpaused.
|
|
||||||
*/
|
|
||||||
void setWebSocketPaused (bool paused);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the WebSocket.
|
|
||||||
*/
|
|
||||||
void closeWebSocket (quint16 reasonCode = NormalClosure, const char* reason = 0);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -148,36 +121,14 @@ protected slots:
|
||||||
*/
|
*/
|
||||||
void readContent ();
|
void readContent ();
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads any incoming WebSocket frames.
|
|
||||||
*/
|
|
||||||
void readFrames ();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/** The available WebSocket frame opcodes. */
|
|
||||||
enum FrameOpcode { ContinuationFrame, TextFrame, BinaryFrame,
|
|
||||||
ConnectionClose = 0x08, Ping, Pong };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to read a single WebSocket frame, returning true if successful.
|
|
||||||
*/
|
|
||||||
bool maybeReadFrame ();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a WebSocket frame header.
|
|
||||||
*/
|
|
||||||
void writeFrameHeader (FrameOpcode opcode, int size = 0, bool final = true);
|
|
||||||
|
|
||||||
/** The parent HTTP manager. */
|
/** The parent HTTP manager. */
|
||||||
HttpManager* _parentManager;
|
HttpManager* _parentManager;
|
||||||
|
|
||||||
/** The underlying socket. */
|
/** The underlying socket. */
|
||||||
QTcpSocket* _socket;
|
QTcpSocket* _socket;
|
||||||
|
|
||||||
/** The mask filter for WebSocket frames. */
|
|
||||||
MaskFilter* _unmasker;
|
|
||||||
|
|
||||||
/** The data stream for writing to the socket. */
|
/** The data stream for writing to the socket. */
|
||||||
QDataStream _stream;
|
QDataStream _stream;
|
||||||
|
|
||||||
|
@ -198,64 +149,6 @@ protected:
|
||||||
|
|
||||||
/** The content of the request. */
|
/** The content of the request. */
|
||||||
QByteArray _requestContent;
|
QByteArray _requestContent;
|
||||||
|
|
||||||
/** The opcode for the WebSocket message being continued. */
|
|
||||||
FrameOpcode _continuingOpcode;
|
|
||||||
|
|
||||||
/** The WebSocket message being continued. */
|
|
||||||
QByteArray _continuingMessage;
|
|
||||||
|
|
||||||
/** Whether or not the WebSocket is paused (buffering messages for future processing). */
|
|
||||||
bool _webSocketPaused;
|
|
||||||
|
|
||||||
/** Whether or not we've sent a WebSocket close message. */
|
|
||||||
bool _closeSent;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A filter device that applies a 32-bit mask.
|
|
||||||
*/
|
|
||||||
class MaskFilter : public QIODevice
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new masker to filter the supplied device.
|
|
||||||
*/
|
|
||||||
MaskFilter (QIODevice* device, QObject* parent = 0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the mask to apply.
|
|
||||||
*/
|
|
||||||
void setMask (quint32 mask);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of bytes available to read.
|
|
||||||
*/
|
|
||||||
virtual qint64 bytesAvailable () const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads masked data from the underlying device.
|
|
||||||
*/
|
|
||||||
virtual qint64 readData (char* data, qint64 maxSize);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes masked data to the underlying device.
|
|
||||||
*/
|
|
||||||
virtual qint64 writeData (const char* data, qint64 maxSize);
|
|
||||||
|
|
||||||
/** The underlying device. */
|
|
||||||
QIODevice* _device;
|
|
||||||
|
|
||||||
/** The current mask. */
|
|
||||||
char _mask[4];
|
|
||||||
|
|
||||||
/** The current position within the mask. */
|
|
||||||
int _position;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HTTP_CONNECTION
|
#endif // HTTP_CONNECTION
|
||||||
|
|
|
@ -10,40 +10,59 @@
|
||||||
// (https://github.com/ey6es/witgap/tree/master/src/cpp/server/http)
|
// (https://github.com/ey6es/witgap/tree/master/src/cpp/server/http)
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <QTcpSocket>
|
#include <QtCore/QDebug>
|
||||||
#include <QtDebug>
|
#include <QtCore/QFile>
|
||||||
|
#include <QtCore/QFileInfo>
|
||||||
|
#include <QtCore/QMimeDatabase>
|
||||||
|
#include <QtNetwork/QTcpSocket>
|
||||||
|
|
||||||
#include "HttpConnection.h"
|
#include "HttpConnection.h"
|
||||||
#include "HttpManager.h"
|
#include "HttpManager.h"
|
||||||
|
|
||||||
void HttpSubrequestHandler::registerSubhandler (const QString& name, HttpRequestHandler* handler) {
|
bool HttpManager::handleRequest(HttpConnection* connection, const QString& path) {
|
||||||
_subhandlers.insert(name, handler);
|
QString subPath = path;
|
||||||
}
|
|
||||||
|
// remove any slash at the beginning of the path
|
||||||
bool HttpSubrequestHandler::handleRequest (
|
if (subPath.startsWith('/')) {
|
||||||
HttpConnection* connection, const QString& name, const QString& path) {
|
subPath.remove(0, 1);
|
||||||
QString subpath = path;
|
|
||||||
if (subpath.startsWith('/')) {
|
|
||||||
subpath.remove(0, 1);
|
|
||||||
}
|
}
|
||||||
QString subname;
|
|
||||||
int idx = subpath.indexOf('/');
|
QString filePath;
|
||||||
if (idx == -1) {
|
|
||||||
subname = subpath;
|
// if the last thing is a trailing slash then we want to look for index file
|
||||||
subpath = "";
|
if (subPath.endsWith('/') || subPath.size() == 0) {
|
||||||
|
QStringList possibleIndexFiles = QStringList() << "index.html" << "index.shtml";
|
||||||
|
|
||||||
|
foreach (const QString& possibleIndexFilename, possibleIndexFiles) {
|
||||||
|
if (QFileInfo(_documentRoot + subPath + possibleIndexFilename).exists()) {
|
||||||
|
filePath = _documentRoot + subPath + possibleIndexFilename;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (QFileInfo(_documentRoot + subPath).isFile()) {
|
||||||
|
filePath = _documentRoot + subPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filePath.isEmpty()) {
|
||||||
|
static QMimeDatabase mimeDatabase;
|
||||||
|
|
||||||
|
qDebug() << "Serving file at" << filePath;
|
||||||
|
|
||||||
|
QFile localFile(filePath);
|
||||||
|
localFile.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
connection->respond("200 OK", localFile.readAll(), qPrintable(mimeDatabase.mimeTypeForFile(filePath).name()));
|
||||||
} else {
|
} else {
|
||||||
subname = subpath.left(idx);
|
// respond with a 404
|
||||||
subpath = subpath.mid(idx + 1);
|
|
||||||
}
|
|
||||||
HttpRequestHandler* handler = _subhandlers.value(subname);
|
|
||||||
if (handler == 0 || !handler->handleRequest(connection, subname, subpath)) {
|
|
||||||
connection->respond("404 Not Found", "Resource not found.");
|
connection->respond("404 Not Found", "Resource not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpManager::HttpManager(quint16 port, QObject* parent) :
|
HttpManager::HttpManager(quint16 port, const QString& documentRoot, QObject* parent) :
|
||||||
QTcpServer(parent) {
|
QTcpServer(parent),
|
||||||
|
_documentRoot(documentRoot) {
|
||||||
// start listening on the passed port
|
// start listening on the passed port
|
||||||
if (!listen(QHostAddress("0.0.0.0"), port)) {
|
if (!listen(QHostAddress("0.0.0.0"), port)) {
|
||||||
qDebug() << "Failed to open HTTP server socket:" << errorString();
|
qDebug() << "Failed to open HTTP server socket:" << errorString();
|
||||||
|
@ -54,7 +73,7 @@ HttpManager::HttpManager(quint16 port, QObject* parent) :
|
||||||
connect(this, SIGNAL(newConnection()), SLOT(acceptConnections()));
|
connect(this, SIGNAL(newConnection()), SLOT(acceptConnections()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpManager::acceptConnections () {
|
void HttpManager::acceptConnections() {
|
||||||
QTcpSocket* socket;
|
QTcpSocket* socket;
|
||||||
while ((socket = nextPendingConnection()) != 0) {
|
while ((socket = nextPendingConnection()) != 0) {
|
||||||
new HttpConnection(socket, this);
|
new HttpConnection(socket, this);
|
||||||
|
|
|
@ -20,48 +20,10 @@
|
||||||
class HttpConnection;
|
class HttpConnection;
|
||||||
class HttpRequestHandler;
|
class HttpRequestHandler;
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for HTTP request handlers.
|
|
||||||
*/
|
|
||||||
class HttpRequestHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles an HTTP request.
|
|
||||||
*/
|
|
||||||
virtual bool handleRequest (
|
|
||||||
HttpConnection* connection, const QString& name, const QString& path) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles requests by forwarding them to subhandlers.
|
|
||||||
*/
|
|
||||||
class HttpSubrequestHandler : public HttpRequestHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a subhandler with the given name.
|
|
||||||
*/
|
|
||||||
void registerSubhandler (const QString& name, HttpRequestHandler* handler);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles an HTTP request.
|
|
||||||
*/
|
|
||||||
virtual bool handleRequest (
|
|
||||||
HttpConnection* connection, const QString& name, const QString& path);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/** Subhandlers mapped by name. */
|
|
||||||
QHash<QString, HttpRequestHandler*> _subhandlers;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles HTTP connections.
|
* Handles HTTP connections.
|
||||||
*/
|
*/
|
||||||
class HttpManager : public QTcpServer, public HttpSubrequestHandler
|
class HttpManager : public QTcpServer
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -70,7 +32,13 @@ public:
|
||||||
/**
|
/**
|
||||||
* Initializes the manager.
|
* Initializes the manager.
|
||||||
*/
|
*/
|
||||||
HttpManager(quint16 port, QObject* parent = 0);
|
HttpManager(quint16 port, const QString& documentRoot, QObject* parent = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles an HTTP request.
|
||||||
|
*/
|
||||||
|
virtual bool handleRequest (HttpConnection* connection, const QString& path);
|
||||||
|
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
|
|
||||||
|
@ -78,6 +46,8 @@ protected slots:
|
||||||
* Accepts all pending connections.
|
* Accepts all pending connections.
|
||||||
*/
|
*/
|
||||||
void acceptConnections ();
|
void acceptConnections ();
|
||||||
|
protected:
|
||||||
|
QString _documentRoot;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HTTP_MANAGER
|
#endif // HTTP_MANAGER
|
||||||
|
|
Loading…
Reference in a new issue