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