/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "minihttpserver.h" #include #include #include #include #include MiniHttpServer::MiniHttpServer(QObject *parent) : QThread(parent) { readyToGo = new QSemaphore; start(); readyToGo->acquire(); delete readyToGo; } MiniHttpServer::~MiniHttpServer() { quitObject->deleteLater(); wait(); } void MiniHttpServer::run() { server = new QTcpServer; server->listen(QHostAddress::LocalHost); portnum = server->serverPort(); connect(server, SIGNAL(newConnection()), this, SLOT(handleConnection()), Qt::DirectConnection); quitObject = new QObject; connect(quitObject, SIGNAL(destroyed()), this, SLOT(quit()), Qt::DirectConnection); readyToGo->release(); exec(); // cleanup delete server; } void MiniHttpServer::handleConnection() { while (server->hasPendingConnections()) { QTcpSocket *socket = server->nextPendingConnection(); new MiniHttpServerConnection(socket); // handles its own lifetime } } MiniHttpServerConnection::MiniHttpServerConnection(QTcpSocket *socket) : QObject(socket), socket(socket), source(0) { connect(socket, SIGNAL(readyRead()), SLOT(handleReadyRead())); connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(handleBytesWritten())); connect(socket, SIGNAL(disconnected()), SLOT(handleDisconnected())); timeout.setInterval(30000); timeout.setSingleShot(true); connect(&timeout, SIGNAL(timeout()), SLOT(handleTimeout())); timeout.start(); } void MiniHttpServerConnection::sendError500() { static const char body[] = "HTTP/1.1 500 Server Error\r\n" "Connection: close\r\n" "Content-Length: 0\r\n" "\r\n"; socket->write(body, strlen(body)); socket->disconnectFromHost(); } void MiniHttpServerConnection::sendError404() { static const char body1[] = "HTTP/1.1 404 File not found\r\n" "Content-Length: 0\r\n"; socket->write(body1, strlen(body1)); if (connectionClose) { socket->write("Connection: close\r\n\r\n"); socket->disconnectFromHost(); } else { socket->write("\r\n"); handlePendingRequest(); } } void MiniHttpServerConnection::handlePendingRequest() { int endOfRequest = buffer.indexOf("\r\n\r\n"); if (endOfRequest == -1) return; // nothing to do QByteArray request = buffer.left(endOfRequest); buffer = buffer.mid(endOfRequest + 4); //qDebug("request: %s", request.constData()); if (!request.startsWith("GET ")) { sendError500(); return; } int eol = request.indexOf("\r\n"); static const char http11[] = " HTTP/1.1"; if (memcmp(request.data() + eol - strlen(http11), http11, strlen(http11)) != 0) { sendError500(); return; } QUrl uri = QUrl::fromEncoded(request.mid(4, eol - strlen(http11) - 4)); source.setFileName(":" + uri.path()); // connection-close? request = request.toLower(); connectionClose = request.contains("\r\nconnection: close\r\n"); if (!source.open(QIODevice::ReadOnly)) { sendError404(); return; } // success timeout.stop(); static const char body[] = "HTTP/1.1 200 Ok\r\n" "Content-Type: text/plain\r\n" "Content-Length: "; socket->write(body, strlen(body)); socket->write(QByteArray::number(source.size())); if (connectionClose) socket->write("\r\nConnection: close"); socket->write("\r\n\r\n"); handleBytesWritten(); } void MiniHttpServerConnection::handleReadyRead() { buffer += socket->readAll(); if (!source.isOpen()) handlePendingRequest(); } void MiniHttpServerConnection::handleDisconnected() { socket->deleteLater(); // will delete us too } void MiniHttpServerConnection::handleBytesWritten() { qint64 maxBytes = qMin(128*1024, source.bytesAvailable()); maxBytes = qMin(maxBytes, 128*1024 - socket->bytesToWrite()); if (maxBytes < 0) return; socket->write(source.read(maxBytes)); if (source.atEnd()) { // file ended source.close(); if (connectionClose) { socket->disconnectFromHost(); } else { timeout.start(); handlePendingRequest(); } } } void MiniHttpServerConnection::handleTimeout() { socket->disconnectFromHost(); }