/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** 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, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $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 - int(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(); }