Qt 5 の未来は明るいブログ

C++ のシンタックスハイライト対応

Published: 2012-12-26

300 行くらい JavaScript 書いて C++ も色がつくようになった。やる気++。

/* Copyright (c) 2012 QtHttpServer Project.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the QtHttpServer nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL QTHTTPSERVER BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef QHTTPCONNECTION_H
#define QHTTPCONNECTION_H

#include <QtNetwork/QTcpSocket>

class QHttpRequest;
class QHttpReply;
class QWebSocket;

class QHttpConnection : public QTcpSocket
{
    Q_OBJECT
public:
#if QT_VERSION < 0x050000
    explicit QHttpConnection(int socketDescriptor, QObject *parent = 0);
#else
    explicit QHttpConnection(qintptr socketDescriptor, QObject *parent = 0);
#endif
    ~QHttpConnection();

    const QHttpRequest *requestFor(QHttpReply *reply);

signals:
    void ready(QHttpRequest *request, QHttpReply *reply);
    void ready(QWebSocket *socket);

private:
    class Private;
    Private *d;
};

#endif // QHTTPCONNECTION_H

/* Copyright (c) 2012 QtHttpServer Project.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the QtHttpServer nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL QTHTTPSERVER BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "qhttpconnection_p.h"

#include <QtCore/QTime>
#include <QtCore/QUrl>

#include "qhttprequest.h"
#include "qhttpreply.h"
#include "qwebsocket.h"

class QHttpConnection::Private : public QObject
{
    Q_OBJECT
public:
    Private(QHttpConnection *parent);

private slots:
    void upgrade(const QByteArray &to, const QUrl &url, const QHash<QByteArray, QByteArray> &rawHeaders);
    void requestReady();
    void replyDone(QObject *);
    void websocketReady();

private:
    QHttpConnection *q;
    int keepAlive;

public:
    QMap<QObject*, QHttpRequest*> requestMap;
    QTime timer;
};

QHttpConnection::Private::Private(QHttpConnection *parent)
    : QObject(parent)
    , q(parent)
    , keepAlive(100)
{
    QHttpRequest *request = new QHttpRequest(q);
    connect(request, SIGNAL(ready()), this, SLOT(requestReady()));
    connect(request, SIGNAL(upgrade(QByteArray, QUrl, QHash<QByteArray, QByteArray>)), this, SLOT(upgrade(QByteArray, QUrl, QHash<QByteArray, QByteArray>)));

    timer.start();
    connect(q, SIGNAL(disconnected()), q, SLOT(deleteLater()));
}

void QHttpConnection::Private::upgrade(const QByteArray &to, const QUrl &url, const QHash<QByteArray, QByteArray> &rawHeaders)
{
    QHttpRequest *request = qobject_cast<QHttpRequest *>(sender());
    disconnect(request, 0, this, 0);
    request->deleteLater();
    if (to.toLower() == "websocket") {
        QWebSocket *socket = new QWebSocket(q, url, rawHeaders);
        connect(socket, SIGNAL(ready()), this, SLOT(websocketReady()));
    }
}

void QHttpConnection::Private::requestReady()
{
    QHttpRequest *request = qobject_cast<QHttpRequest *>(sender());
    disconnect(request, SIGNAL(ready()), this, SLOT(requestReady()));
    QHttpReply *reply = new QHttpReply(q);
    connect(reply, SIGNAL(destroyed(QObject *)), this, SLOT(replyDone(QObject*)));
    requestMap.insert(reply, request);
    emit q->ready(request, reply);

    if (request->hasRawHeader("Connection")) {
//        qDebug() << request->rawHeader("Connection") << keepAlive;
        if (request->rawHeader("Connection") == QByteArray("Keep-Alive").toLower()) {
            if (keepAlive > 0) {
                reply->setRawHeader("Keep-Alive", QString::fromUtf8("timeout=1, max=%1").arg(keepAlive--).toUtf8());
                reply->setRawHeader("Connection", "Keep-Alive");
                request = new QHttpRequest(q);
                connect(request, SIGNAL(ready()), this, SLOT(requestReady()));
            } else {
                reply->setRawHeader("Connection", "Close");
                keepAlive = 0;
            }
        } else {
            reply->setRawHeader("Connection", "Close");
            keepAlive = 0;
        }
    } else {
        reply->setRawHeader("Connection", "Close");
        keepAlive = 0;
    }
}

void QHttpConnection::Private::replyDone(QObject *reply)
{
    if (requestMap.contains(reply)) {
        QHttpRequest *request = requestMap.take(reply);
        request->deleteLater();
    }
    if (keepAlive == 0 && requestMap.isEmpty()) {
        q->disconnectFromHost();
    }
}

void QHttpConnection::Private::websocketReady()
{
    QWebSocket *socket = qobject_cast<QWebSocket *>(sender());
    disconnect(socket, SIGNAL(ready()), this, SLOT(websocketReady()));
    emit q->ready(socket);
}

#if QT_VERSION < 0x050000
QHttpConnection::QHttpConnection(int socketDescriptor, QObject *parent)
#else
QHttpConnection::QHttpConnection(qintptr socketDescriptor, QObject *parent)
#endif
    : QTcpSocket(parent)
    , d(new Private(this))
{
    setSocketOption(KeepAliveOption, 1);
    setSocketDescriptor(socketDescriptor);
}

QHttpConnection::~QHttpConnection()
{
//    qDebug() << d << socketDescriptor() << d->timer.elapsed();
//    qDebug() << d->timer.elapsed();
}

const QHttpRequest *QHttpConnection::requestFor(QHttpReply *reply)
{
    return d->requestMap.value(reply);
}

#include "qhttpconnection.moc"