PageRenderTime 317ms CodeModel.GetById 50ms app.highlight 212ms RepoModel.GetById 40ms app.codeStats 1ms

/thirdparty/qxt/qxtweb-standalone/qxtweb/qxtabstracthttpconnector.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 249 lines | 126 code | 22 blank | 101 comment | 12 complexity | 9d37f573e2db34f4eebb30dc8be85889 MD5 | raw file
  1/****************************************************************************
  2 **
  3 ** Copyright (C) Qxt Foundation. Some rights reserved.
  4 **
  5 ** This file is part of the QxtWeb module of the Qxt library.
  6 **
  7 ** This library is free software; you can redistribute it and/or modify it
  8 ** under the terms of the Common Public License, version 1.0, as published
  9 ** by IBM, and/or under the terms of the GNU Lesser General Public License,
 10 ** version 2.1, as published by the Free Software Foundation.
 11 **
 12 ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
 13 ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
 14 ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
 15 ** FITNESS FOR A PARTICULAR PURPOSE.
 16 **
 17 ** You should have received a copy of the CPL and the LGPL along with this
 18 ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
 19 ** included with the source distribution for more information.
 20 ** If you did not receive a copy of the licenses, contact the Qxt Foundation.
 21 **
 22 ** <http://libqxt.org>  <foundation@libqxt.org>
 23 **
 24 ****************************************************************************/
 25
 26/*!
 27\class QxtAbstractHttpConnector
 28
 29\inmodule QxtWeb
 30
 31\brief The QxtAbstractHttpConnector class is a base class for defining 
 32HTTP-based protocols for use with QxtHttpSessionManager
 33
 34QxtHttpSessionManager does the work of managing sessions and state for the
 35otherwise stateless HTTP protocol, but it relies on QxtAbstractHttpConnector
 36subclasses to implement the protocol used to communicate with the web server.
 37
 38Subclasses are responsible for accepting new connections (by implementing
 39listen(const QHostAddress&, quint16) and invoking addConnection(QIODevice*)),
 40for informing the session manager when request headers are available (by
 41implementing canParseRequest(const QByteArray&)), for parsing the request
 42headers (by implementing parseRequest(QByteArray&)), and for writing response
 43headers (by implementing writeHeaders(QIODevice*, const QHttpResponseHeader&)).
 44
 45\sa QxtHttpSessionManager
 46*/
 47
 48#include "qxthttpsessionmanager.h"
 49#include "qxtwebcontent.h"
 50#include <QReadWriteLock>
 51#include <QHash>
 52#include <QIODevice>
 53#include <QByteArray>
 54
 55#ifndef QXT_DOXYGEN_RUN
 56class QxtAbstractHttpConnectorPrivate : public QxtPrivate<QxtAbstractHttpConnector>
 57{
 58public:
 59    QxtHttpSessionManager* manager;
 60    QReadWriteLock bufferLock, requestLock;
 61    QHash<QIODevice*, QByteArray> buffers;  // connection->buffer
 62    QHash<quint32, QIODevice*> requests;    // requestID->connection
 63    QHash<quint32, QSharedPointer<QIODevice> > dataSources; // iodevices providing result data
 64    quint32 nextRequestID;
 65
 66    inline quint32 getNextRequestID(QIODevice* connection)
 67    {
 68        QWriteLocker locker(&requestLock);
 69        do
 70        {
 71            nextRequestID++;
 72            if (nextRequestID == 0xFFFFFFFF) nextRequestID = 1;
 73        }
 74        while (requests.contains(nextRequestID)); // yeah, right
 75        requests[nextRequestID] = connection;
 76        return nextRequestID;
 77    }
 78
 79    inline void doneWithRequest(quint32 requestID)
 80    {
 81        QWriteLocker locker(&requestLock);
 82        requests.remove(requestID);
 83        dataSources.remove(requestID);
 84    }
 85
 86    inline QIODevice* getRequestConnection(quint32 requestID)
 87    {
 88        QReadLocker locker(&requestLock);
 89        return requests[requestID];
 90    }
 91
 92    inline void setRequestDataSource( quint32 requestID, QSharedPointer<QIODevice>& dataSource )
 93    {
 94        QWriteLocker locker(&requestLock);
 95        dataSources.insert( requestID, dataSource );
 96    }
 97
 98    inline QSharedPointer<QIODevice>& getRequestDataSource(quint32 requestID)
 99    {
100        QReadLocker locker(&requestLock);
101        return dataSources[requestID];
102    }
103};
104#endif
105
106/*!
107 * Creates a QxtAbstractHttpConnector with the specified \a parent.
108 *
109 * Note that this is an abstract class and cannot be instantiated directly.
110 */
111QxtAbstractHttpConnector::QxtAbstractHttpConnector(QObject* parent) : QObject(parent)
112{
113    QXT_INIT_PRIVATE(QxtAbstractHttpConnector);
114    qxt_d().nextRequestID = 0;
115}
116
117/*!
118 * \internal
119 */
120void QxtAbstractHttpConnector::setSessionManager(QxtHttpSessionManager* manager)
121{
122    qxt_d().manager = manager;
123}
124
125/*!
126 * Returns the session manager into which the connector is installed.
127 *
128 * \sa QxtHttpSessionManager::setConnector
129 */
130QxtHttpSessionManager* QxtAbstractHttpConnector::sessionManager() const
131{
132    return qxt_d().manager;
133}
134
135/*!
136 * \internal
137 * Returns the QIODevice associated with a \a requestID.
138 *
139 * The request ID is generated internally and used by the session manager.
140 */
141QIODevice* QxtAbstractHttpConnector::getRequestConnection(quint32 requestID)
142{
143    return qxt_d().getRequestConnection(requestID);
144}
145
146void QxtAbstractHttpConnector::setRequestDataSource( quint32 requestID, QSharedPointer<QIODevice>& dataSource )
147{
148    qxt_d().setRequestDataSource( requestID, dataSource );
149}
150
151QSharedPointer<QIODevice>& QxtAbstractHttpConnector::getRequestDataSource(quint32 requestID)
152{
153    return qxt_d().getRequestDataSource( requestID );
154}
155
156void QxtAbstractHttpConnector::doneWithRequest(quint32 requestID)
157{
158    qxt_d().doneWithRequest( requestID );
159}
160
161/*!
162 * Starts managing a new connection from \a device.
163 *
164 * This function should be invoked by a subclass to attach incoming connections
165 * to the session manager.
166 */
167void QxtAbstractHttpConnector::addConnection(QIODevice* device)
168{
169    QWriteLocker locker(&qxt_d().bufferLock);
170    qxt_d().buffers[device] = QByteArray();
171    QObject::connect(device, SIGNAL(readyRead()), this, SLOT(incomingData()));
172    QObject::connect(device, SIGNAL(aboutToClose()), this, SLOT(disconnected()));
173    QObject::connect(device, SIGNAL(disconnected()), this, SLOT(disconnected()));
174    QObject::connect(device, SIGNAL(destroyed()), this, SLOT(disconnected()));
175}
176
177/*!
178 * \internal
179 */
180void QxtAbstractHttpConnector::incomingData(QIODevice* device)
181{
182    if (!device)
183    {
184        device = qobject_cast<QIODevice*>(sender());
185        if (!device) return;
186    }
187    QReadLocker locker(&qxt_d().bufferLock);
188    QByteArray& buffer = qxt_d().buffers[device];
189    buffer.append(device->readAll());
190    if (!canParseRequest(buffer)) return;
191    QHttpRequestHeader header = parseRequest(buffer);
192    QxtWebContent* content = 0;
193    QByteArray start;
194    if (header.contentLength() > 0)
195    {
196        start = buffer.left(header.value("content-length").toInt());
197        buffer = buffer.mid(header.value("content-length").toInt());
198        content = new QxtWebContent(header.contentLength(), start, device);
199    }
200    else if (header.hasKey("connection") && header.value("connection").toLower() == "close")
201    {
202        start = buffer;
203        buffer.clear();
204        content = new QxtWebContent(header.contentLength(), start, device);
205    } // else no content
206    quint32 requestID = qxt_d().getNextRequestID(device);
207    sessionManager()->incomingRequest(requestID, header, content);
208}
209
210/*!
211 * \internal
212 */
213void QxtAbstractHttpConnector::disconnected()
214{
215    QIODevice* device = qobject_cast<QIODevice*>(sender());
216    if (!device) return;
217    QWriteLocker locker(&qxt_d().bufferLock);
218    qxt_d().buffers.remove(device);
219    sessionManager()->disconnected(device);
220}
221
222/*!
223 * \fn virtual bool QxtAbstractHttpConnector::listen(const QHostAddress& interface, quint16 port)
224 * Invoked by the session manager to indicate that the connector should listen
225 * for incoming connections on the specified \a interface and \a port.
226 *
227 * If the interface is QHostAddress::Any, the server will listen on all network interfaces.
228 *
229 * Returns true on success, or false if the server could not begin listening.
230 *
231 * \sa addConnection(QIODevice*)
232 */
233
234/*!
235 * \fn virtual bool QxtAbstractHttpConnector::canParseRequest(const QByteArray& buffer)
236 * Returns true if a complete set of request headers can be extracted from the provided \a buffer.
237 */
238
239/*!
240 * \fn virtual QHttpRequestHeader QxtAbstractHttpConnector::parseRequest(QByteArray& buffer)
241 * Extracts a set of request headers from the provided \a buffer.
242 *
243 * Subclasses implementing this function must be sure to remove the parsed data from the buffer.
244 */
245
246/*!
247 * \fn virtual void QxtAbstractHttpConnector::writeHeaders(QIODevice* device, const QHttpResponseHeader& header)
248 * Writes a the response \a header to the specified \a device.
249 */