/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. \class QxtAbstractHttpConnector
  27. \inmodule QxtWeb
  28. \brief The QxtAbstractHttpConnector class is a base class for defining
  29. HTTP-based protocols for use with QxtHttpSessionManager
  30. QxtHttpSessionManager does the work of managing sessions and state for the
  31. otherwise stateless HTTP protocol, but it relies on QxtAbstractHttpConnector
  32. subclasses to implement the protocol used to communicate with the web server.
  33. Subclasses are responsible for accepting new connections (by implementing
  34. listen(const QHostAddress&, quint16) and invoking addConnection(QIODevice*)),
  35. for informing the session manager when request headers are available (by
  36. implementing canParseRequest(const QByteArray&)), for parsing the request
  37. headers (by implementing parseRequest(QByteArray&)), and for writing response
  38. headers (by implementing writeHeaders(QIODevice*, const QHttpResponseHeader&)).
  39. \sa QxtHttpSessionManager
  40. */
  41. #include "qxthttpsessionmanager.h"
  42. #include "qxtwebcontent.h"
  43. #include <QReadWriteLock>
  44. #include <QHash>
  45. #include <QIODevice>
  46. #include <QByteArray>
  47. #ifndef QXT_DOXYGEN_RUN
  48. class QxtAbstractHttpConnectorPrivate : public QxtPrivate<QxtAbstractHttpConnector>
  49. {
  50. public:
  51. QxtHttpSessionManager* manager;
  52. QReadWriteLock bufferLock, requestLock;
  53. QHash<QIODevice*, QByteArray> buffers; // connection->buffer
  54. QHash<quint32, QIODevice*> requests; // requestID->connection
  55. QHash<quint32, QSharedPointer<QIODevice> > dataSources; // iodevices providing result data
  56. quint32 nextRequestID;
  57. inline quint32 getNextRequestID(QIODevice* connection)
  58. {
  59. QWriteLocker locker(&requestLock);
  60. do
  61. {
  62. nextRequestID++;
  63. if (nextRequestID == 0xFFFFFFFF) nextRequestID = 1;
  64. }
  65. while (requests.contains(nextRequestID)); // yeah, right
  66. requests[nextRequestID] = connection;
  67. return nextRequestID;
  68. }
  69. inline void doneWithRequest(quint32 requestID)
  70. {
  71. QWriteLocker locker(&requestLock);
  72. requests.remove(requestID);
  73. dataSources.remove(requestID);
  74. }
  75. inline QIODevice* getRequestConnection(quint32 requestID)
  76. {
  77. QReadLocker locker(&requestLock);
  78. return requests[requestID];
  79. }
  80. inline void setRequestDataSource( quint32 requestID, QSharedPointer<QIODevice>& dataSource )
  81. {
  82. QWriteLocker locker(&requestLock);
  83. dataSources.insert( requestID, dataSource );
  84. }
  85. inline QSharedPointer<QIODevice>& getRequestDataSource(quint32 requestID)
  86. {
  87. QReadLocker locker(&requestLock);
  88. return dataSources[requestID];
  89. }
  90. };
  91. #endif
  92. /*!
  93. * Creates a QxtAbstractHttpConnector with the specified \a parent.
  94. *
  95. * Note that this is an abstract class and cannot be instantiated directly.
  96. */
  97. QxtAbstractHttpConnector::QxtAbstractHttpConnector(QObject* parent) : QObject(parent)
  98. {
  99. QXT_INIT_PRIVATE(QxtAbstractHttpConnector);
  100. qxt_d().nextRequestID = 0;
  101. }
  102. /*!
  103. * \internal
  104. */
  105. void QxtAbstractHttpConnector::setSessionManager(QxtHttpSessionManager* manager)
  106. {
  107. qxt_d().manager = manager;
  108. }
  109. /*!
  110. * Returns the session manager into which the connector is installed.
  111. *
  112. * \sa QxtHttpSessionManager::setConnector
  113. */
  114. QxtHttpSessionManager* QxtAbstractHttpConnector::sessionManager() const
  115. {
  116. return qxt_d().manager;
  117. }
  118. /*!
  119. * \internal
  120. * Returns the QIODevice associated with a \a requestID.
  121. *
  122. * The request ID is generated internally and used by the session manager.
  123. */
  124. QIODevice* QxtAbstractHttpConnector::getRequestConnection(quint32 requestID)
  125. {
  126. return qxt_d().getRequestConnection(requestID);
  127. }
  128. void QxtAbstractHttpConnector::setRequestDataSource( quint32 requestID, QSharedPointer<QIODevice>& dataSource )
  129. {
  130. qxt_d().setRequestDataSource( requestID, dataSource );
  131. }
  132. QSharedPointer<QIODevice>& QxtAbstractHttpConnector::getRequestDataSource(quint32 requestID)
  133. {
  134. return qxt_d().getRequestDataSource( requestID );
  135. }
  136. void QxtAbstractHttpConnector::doneWithRequest(quint32 requestID)
  137. {
  138. qxt_d().doneWithRequest( requestID );
  139. }
  140. /*!
  141. * Starts managing a new connection from \a device.
  142. *
  143. * This function should be invoked by a subclass to attach incoming connections
  144. * to the session manager.
  145. */
  146. void QxtAbstractHttpConnector::addConnection(QIODevice* device)
  147. {
  148. QWriteLocker locker(&qxt_d().bufferLock);
  149. qxt_d().buffers[device] = QByteArray();
  150. QObject::connect(device, SIGNAL(readyRead()), this, SLOT(incomingData()));
  151. QObject::connect(device, SIGNAL(aboutToClose()), this, SLOT(disconnected()));
  152. QObject::connect(device, SIGNAL(disconnected()), this, SLOT(disconnected()));
  153. QObject::connect(device, SIGNAL(destroyed()), this, SLOT(disconnected()));
  154. }
  155. /*!
  156. * \internal
  157. */
  158. void QxtAbstractHttpConnector::incomingData(QIODevice* device)
  159. {
  160. if (!device)
  161. {
  162. device = qobject_cast<QIODevice*>(sender());
  163. if (!device) return;
  164. }
  165. QReadLocker locker(&qxt_d().bufferLock);
  166. QByteArray& buffer = qxt_d().buffers[device];
  167. buffer.append(device->readAll());
  168. if (!canParseRequest(buffer)) return;
  169. QHttpRequestHeader header = parseRequest(buffer);
  170. QxtWebContent* content = 0;
  171. QByteArray start;
  172. if (header.contentLength() > 0)
  173. {
  174. start = buffer.left(header.value("content-length").toInt());
  175. buffer = buffer.mid(header.value("content-length").toInt());
  176. content = new QxtWebContent(header.contentLength(), start, device);
  177. }
  178. else if (header.hasKey("connection") && header.value("connection").toLower() == "close")
  179. {
  180. start = buffer;
  181. buffer.clear();
  182. content = new QxtWebContent(header.contentLength(), start, device);
  183. } // else no content
  184. quint32 requestID = qxt_d().getNextRequestID(device);
  185. sessionManager()->incomingRequest(requestID, header, content);
  186. }
  187. /*!
  188. * \internal
  189. */
  190. void QxtAbstractHttpConnector::disconnected()
  191. {
  192. QIODevice* device = qobject_cast<QIODevice*>(sender());
  193. if (!device) return;
  194. QWriteLocker locker(&qxt_d().bufferLock);
  195. qxt_d().buffers.remove(device);
  196. sessionManager()->disconnected(device);
  197. }
  198. /*!
  199. * \fn virtual bool QxtAbstractHttpConnector::listen(const QHostAddress& interface, quint16 port)
  200. * Invoked by the session manager to indicate that the connector should listen
  201. * for incoming connections on the specified \a interface and \a port.
  202. *
  203. * If the interface is QHostAddress::Any, the server will listen on all network interfaces.
  204. *
  205. * Returns true on success, or false if the server could not begin listening.
  206. *
  207. * \sa addConnection(QIODevice*)
  208. */
  209. /*!
  210. * \fn virtual bool QxtAbstractHttpConnector::canParseRequest(const QByteArray& buffer)
  211. * Returns true if a complete set of request headers can be extracted from the provided \a buffer.
  212. */
  213. /*!
  214. * \fn virtual QHttpRequestHeader QxtAbstractHttpConnector::parseRequest(QByteArray& buffer)
  215. * Extracts a set of request headers from the provided \a buffer.
  216. *
  217. * Subclasses implementing this function must be sure to remove the parsed data from the buffer.
  218. */
  219. /*!
  220. * \fn virtual void QxtAbstractHttpConnector::writeHeaders(QIODevice* device, const QHttpResponseHeader& header)
  221. * Writes a the response \a header to the specified \a device.
  222. */