PageRenderTime 144ms CodeModel.GetById 40ms app.highlight 42ms RepoModel.GetById 59ms app.codeStats 0ms

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

http://github.com/tomahawk-player/tomahawk
C++ | 259 lines | 135 code | 19 blank | 105 comment | 23 complexity | 55ea5b0e62e89a28717d0d08d0b2189a 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 QxtWebContent
 28
 29\inmodule QxtWeb
 30
 31\brief The QxtWebContent class provides and I/O device for data sent by the web browser
 32
 33QxtWebContent is a read-only QIODevice subclass that encapsulates data sent
 34from the web browser, for instance in a POST or PUT request.
 35
 36In order to avoid delays while reading content sent from the client, and to
 37insulate multiple pipelined requests on the same connection from each other,
 38QxtWeb uses QxtWebContent as an abstraction for streaming data.
 39
 40\sa QxtAbstractWebService
 41*/
 42
 43#include "qxtwebcontent.h"
 44#include <string.h>
 45#include <QUrl>
 46
 47#ifndef QXT_DOXYGEN_RUN
 48class QxtWebContentPrivate : public QxtPrivate<QxtWebContent>
 49{
 50public:
 51    QxtWebContentPrivate() : ignoreRemaining(false) {}
 52    QXT_DECLARE_PUBLIC(QxtWebContent)
 53
 54    void init(int contentLength, const QByteArray& start, QIODevice* device)
 55    {
 56        this->start = start;
 57        this->device = device;
 58        if (contentLength <= 0)
 59            bytesRemaining = -1;
 60        else
 61            bytesRemaining = contentLength - start.length();
 62        if (device)
 63        {
 64            QObject::connect(device, SIGNAL(readyRead()), &qxt_p(), SIGNAL(readyRead()));
 65            // QObject::connect(device, SIGNAL(aboutToClose()), this, SIGNAL(aboutToClose()));
 66            // QObject::connect(device, SIGNAL(destroyed()), this, SIGNAL(aboutToClose()));
 67            // ask the object if it has an error signal
 68            if (device->metaObject()->indexOfSignal(QMetaObject::normalizedSignature(SIGNAL(error(QAbstractSocket::SocketError)))) >= 0)
 69            {
 70                QObject::connect(device, SIGNAL(error(QAbstractSocket::SocketError)), &qxt_p(), SLOT(errorReceived(QAbstractSocket::SocketError)));
 71            }
 72        }
 73        qxt_p().setOpenMode(QIODevice::ReadOnly);
 74    }
 75
 76    qint64 bytesRemaining;
 77    QByteArray start;
 78    QIODevice* device;
 79    bool ignoreRemaining;
 80};
 81#endif
 82
 83/*!
 84 * Constructs a QxtWebContent object.
 85 *
 86 * The content provided by this constructor is the first \a contentLength bytes
 87 * read from the provided \a device.
 88 *
 89 * The QxtWebContent object is parented to the \a device.
 90 */
 91QxtWebContent::QxtWebContent(int contentLength, QIODevice* device) : QIODevice(device)
 92{
 93    QXT_INIT_PRIVATE(QxtWebContent);
 94    qxt_d().init(contentLength, QByteArray(), device);
 95}
 96
 97/*!
 98 * Constructs a QxtWebContent object.
 99 *
100 * The content provided by this constructor is the data contained in \a start,
101 * followed by enough data read from the provided \a device to fill the desired
102 * \a contentLength.
103 *
104 * The QxtWebContent object is parented to the \a device.
105 */
106QxtWebContent::QxtWebContent(int contentLength, const QByteArray& start, QIODevice* device) : QIODevice(device)
107{
108    QXT_INIT_PRIVATE(QxtWebContent);
109    qxt_d().init(contentLength, start, device);
110}
111
112/*!
113 * Constructs a QxtWebContent object with the specified \a parent.
114 *
115 * The content provided by this constructor is exactly the data contained in
116 * \a content.
117 */
118QxtWebContent::QxtWebContent(const QByteArray& content, QObject* parent) : QIODevice(parent)
119{
120    QXT_INIT_PRIVATE(QxtWebContent);
121    qxt_d().init(content.size(), content, 0);
122}
123
124/*!
125 * \reimp
126 */
127qint64 QxtWebContent::bytesAvailable() const
128{
129    qint64 available = QIODevice::bytesAvailable() + (qxt_d().device ? qxt_d().device->bytesAvailable() : 0) + qxt_d().start.count();
130    if (available > qxt_d().bytesRemaining)
131        return qxt_d().bytesRemaining;
132    return available;
133}
134
135/*!
136 * \reimp
137 */
138qint64 QxtWebContent::readData(char* data, qint64 maxSize)
139{
140    char* writePtr = data;
141    // read more than 32k; TCP ideally handles 48k blocks but we need wiggle room
142    if (maxSize > 32768) maxSize = 32768;
143
144    // don't read more than the content-length
145    int sz = qxt_d().start.count();
146    if (sz > 0 && maxSize > sz)
147    {
148        memcpy(writePtr, qxt_d().start.constData(), sz);
149        writePtr += sz;
150        maxSize -= sz;
151        qxt_d().start.clear();
152    }
153    else if (sz > 0 && sz > maxSize)
154    {
155        memcpy(writePtr, qxt_d().start.constData(), maxSize);
156        qxt_d().start = qxt_d().start.mid(maxSize);
157        return maxSize;
158    }
159
160    if (qxt_d().device == 0)
161    {
162        return sz;
163    }
164    else if (qxt_d().bytesRemaining >= 0)
165    {
166        qint64 readBytes = qxt_d().device->read(writePtr, (maxSize > qxt_d().bytesRemaining) ? qxt_d().bytesRemaining : maxSize);
167        qxt_d().bytesRemaining -= readBytes;
168        if (qxt_d().bytesRemaining == 0) QMetaObject::invokeMethod(this, "aboutToClose", Qt::QueuedConnection);
169        return sz + readBytes;
170    }
171    else
172    {
173        return sz + qxt_d().device->read(writePtr, maxSize);
174    }
175}
176
177/*!
178 * Returns the number of bytes of content that have not yet been read.
179 *
180 * Note that not all of the remaining content may be immediately available for
181 * reading. This function returns the content length, minus the number of
182 * bytes that have already been read.
183 */
184qint64 QxtWebContent::unreadBytes() const
185{
186    return qxt_d().start.size() + qxt_d().bytesRemaining;
187}
188
189/*!
190 * \reimp
191 */
192qint64 QxtWebContent::writeData(const char*, qint64)
193{
194    // always an error to write
195    return -1;
196}
197
198/*!
199 * \internal
200 */
201void QxtWebContent::errorReceived(QAbstractSocket::SocketError)
202{
203    setErrorString(qxt_d().device->errorString());
204}
205
206/*!
207 * Blocks until all of the streaming data has been read from the browser.
208 *
209 * Note that this function will block events for the thread on which it is called.
210 * If the main thread is blocked, QxtWeb will be unable to process additional
211 * requests until the content has been received.
212 */
213void QxtWebContent::waitForAllContent()
214{
215    if (!qxt_d().device) return;
216    QByteArray buffer;
217    while (qxt_d().device && qxt_d().bytesRemaining > 0)
218    {
219        buffer = qxt_d().device->readAll();
220        qxt_d().start += buffer;
221        qxt_d().bytesRemaining -= buffer.size();
222        if (qxt_d().bytesRemaining > 0) qxt_d().device->waitForReadyRead(-1);
223    }
224}
225
226/*!
227 * Discards any data not yet read.
228 *
229 * After invoking this function, any further data received from the browser
230 * is silently discarded.
231 */
232void QxtWebContent::ignoreRemainingContent()
233{
234    if (qxt_d().bytesRemaining <= 0 || !qxt_d().device) return;
235    if (!qxt_d().ignoreRemaining)
236    {
237        qxt_d().ignoreRemaining = true;
238        QObject::connect(qxt_d().device, SIGNAL(readyRead()), this, SLOT(ignoreRemainingContent()));
239    }
240}
241
242#ifndef QXT_DOXYGEN_RUN
243typedef QPair<QString, QString> QxtQueryItem;
244#endif
245
246/*!
247 * Extracts the key/value pairs from application/x-www-form-urlencoded \a data,
248 * such as the query string from the URL or the form data from a POST request.
249 */
250QHash<QString, QString> QxtWebContent::parseUrlEncodedQuery(const QString& data)
251{
252    QUrl post("/?" + data);
253    QHash<QString, QString> rv;
254    foreach(const QxtQueryItem& item, post.queryItems())
255    {
256        rv.insertMulti(item.first, item.second);
257    }
258    return rv;
259}