qhttpconnection.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /*
  2. * Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to
  6. * deal in the Software without restriction, including without limitation the
  7. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. * sell copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. * IN THE SOFTWARE.
  21. */
  22. #include "qhttpconnection.h"
  23. #include <QTcpSocket>
  24. #include <QHostAddress>
  25. #include "http_parser.h"
  26. #include "qhttprequest.h"
  27. #include "qhttpresponse.h"
  28. /// @cond nodoc
  29. QHttpConnection::QHttpConnection(QTcpSocket *socket, QObject *parent)
  30. : QObject(parent),
  31. m_socket(socket),
  32. m_parser(0),
  33. m_parserSettings(0),
  34. m_request(0),
  35. m_transmitLen(0),
  36. m_transmitPos(0)
  37. {
  38. m_parser = (http_parser *)malloc(sizeof(http_parser));
  39. http_parser_init(m_parser, HTTP_REQUEST);
  40. m_parserSettings = new http_parser_settings();
  41. m_parserSettings->on_message_begin = MessageBegin;
  42. m_parserSettings->on_url = Url;
  43. m_parserSettings->on_header_field = HeaderField;
  44. m_parserSettings->on_header_value = HeaderValue;
  45. m_parserSettings->on_headers_complete = HeadersComplete;
  46. m_parserSettings->on_body = Body;
  47. m_parserSettings->on_message_complete = MessageComplete;
  48. m_parser->data = this;
  49. connect(socket, SIGNAL(readyRead()), this, SLOT(parseRequest()));
  50. connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
  51. connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(updateWriteCount(qint64)));
  52. }
  53. QHttpConnection::~QHttpConnection()
  54. {
  55. m_socket = 0;
  56. free(m_parser);
  57. m_parser = 0;
  58. delete m_parserSettings;
  59. m_parserSettings = 0;
  60. }
  61. void QHttpConnection::socketDisconnected()
  62. {
  63. deleteLater();
  64. if (m_request) {
  65. if (m_request->successful())
  66. return;
  67. m_request->setSuccessful(false);
  68. Q_EMIT m_request->end();
  69. }
  70. }
  71. void QHttpConnection::updateWriteCount(qint64 count)
  72. {
  73. Q_ASSERT(m_transmitPos + count <= m_transmitLen);
  74. m_transmitPos += count;
  75. if (m_transmitPos == m_transmitLen)
  76. {
  77. m_transmitLen = 0;
  78. m_transmitPos = 0;
  79. Q_EMIT allBytesWritten();
  80. }
  81. }
  82. void QHttpConnection::parseRequest()
  83. {
  84. Q_ASSERT(m_parser);
  85. while (m_socket->bytesAvailable()) {
  86. QByteArray arr = m_socket->readAll();
  87. http_parser_execute(m_parser, m_parserSettings, arr.constData(), arr.size());
  88. }
  89. }
  90. void QHttpConnection::write(const QByteArray &data)
  91. {
  92. m_socket->write(data);
  93. m_transmitLen += data.size();
  94. }
  95. void QHttpConnection::flush()
  96. {
  97. m_socket->flush();
  98. }
  99. void QHttpConnection::waitForBytesWritten()
  100. {
  101. m_socket->waitForBytesWritten();
  102. }
  103. void QHttpConnection::responseDone()
  104. {
  105. QHttpResponse *response = qobject_cast<QHttpResponse *>(QObject::sender());
  106. if (response->m_last)
  107. m_socket->disconnectFromHost();
  108. }
  109. /* URL Utilities */
  110. #define HAS_URL_FIELD(info, field) (info.field_set &(1 << (field)))
  111. #define GET_FIELD(data, info, field) \
  112. QString::fromLatin1(data + info.field_data[field].off, info.field_data[field].len)
  113. #define CHECK_AND_GET_FIELD(data, info, field) \
  114. (HAS_URL_FIELD(info, field) ? GET_FIELD(data, info, field) : QString())
  115. QUrl createUrl(const char *urlData, const http_parser_url &urlInfo)
  116. {
  117. QUrl url;
  118. url.setScheme(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_SCHEMA));
  119. url.setHost(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_HOST));
  120. // Port is dealt with separately since it is available as an integer.
  121. url.setPath(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_PATH));
  122. #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
  123. url.setQuery(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_QUERY));
  124. #else
  125. if (HAS_URL_FIELD(urlInfo, UF_QUERY)) {
  126. url.setEncodedQuery(QByteArray(urlData + urlInfo.field_data[UF_QUERY].off,
  127. urlInfo.field_data[UF_QUERY].len));
  128. }
  129. #endif
  130. url.setFragment(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_FRAGMENT));
  131. url.setUserInfo(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_USERINFO));
  132. if (HAS_URL_FIELD(urlInfo, UF_PORT))
  133. url.setPort(urlInfo.port);
  134. return url;
  135. }
  136. #undef CHECK_AND_SET_FIELD
  137. #undef GET_FIELD
  138. #undef HAS_URL_FIELD
  139. /********************
  140. * Static Callbacks *
  141. *******************/
  142. int QHttpConnection::MessageBegin(http_parser *parser)
  143. {
  144. QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
  145. theConnection->m_currentHeaders.clear();
  146. theConnection->m_currentUrl.clear();
  147. theConnection->m_currentUrl.reserve(128);
  148. // The QHttpRequest should not be parented to this, since it's memory
  149. // management is the responsibility of the user of the library.
  150. theConnection->m_request = new QHttpRequest(theConnection);
  151. return 0;
  152. }
  153. int QHttpConnection::HeadersComplete(http_parser *parser)
  154. {
  155. QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
  156. Q_ASSERT(theConnection->m_request);
  157. /** set method **/
  158. theConnection->m_request->setMethod(static_cast<QHttpRequest::HttpMethod>(parser->method));
  159. /** set version **/
  160. theConnection->m_request->setVersion(
  161. QString("%1.%2").arg(parser->http_major).arg(parser->http_minor));
  162. /** get parsed url **/
  163. struct http_parser_url urlInfo;
  164. int r = http_parser_parse_url(theConnection->m_currentUrl.constData(),
  165. theConnection->m_currentUrl.size(),
  166. parser->method == HTTP_CONNECT, &urlInfo);
  167. Q_ASSERT(r == 0);
  168. Q_UNUSED(r);
  169. theConnection->m_request->setUrl(createUrl(theConnection->m_currentUrl.constData(), urlInfo));
  170. // Insert last remaining header
  171. theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] =
  172. theConnection->m_currentHeaderValue;
  173. theConnection->m_request->setHeaders(theConnection->m_currentHeaders);
  174. /** set client information **/
  175. theConnection->m_request->m_remoteAddress = theConnection->m_socket->peerAddress().toString();
  176. theConnection->m_request->m_remotePort = theConnection->m_socket->peerPort();
  177. QHttpResponse *response = new QHttpResponse(theConnection);
  178. if (parser->http_major < 1 || parser->http_minor < 1)
  179. response->m_keepAlive = false;
  180. connect(theConnection, SIGNAL(destroyed()), response, SLOT(connectionClosed()));
  181. connect(response, SIGNAL(done()), theConnection, SLOT(responseDone()));
  182. // we are good to go!
  183. Q_EMIT theConnection->newRequest(theConnection->m_request, response);
  184. return 0;
  185. }
  186. int QHttpConnection::MessageComplete(http_parser *parser)
  187. {
  188. // TODO: do cleanup and prepare for next request
  189. QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
  190. Q_ASSERT(theConnection->m_request);
  191. theConnection->m_request->setSuccessful(true);
  192. Q_EMIT theConnection->m_request->end();
  193. return 0;
  194. }
  195. int QHttpConnection::Url(http_parser *parser, const char *at, size_t length)
  196. {
  197. QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
  198. Q_ASSERT(theConnection->m_request);
  199. theConnection->m_currentUrl.append(at, length);
  200. return 0;
  201. }
  202. int QHttpConnection::HeaderField(http_parser *parser, const char *at, size_t length)
  203. {
  204. QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
  205. Q_ASSERT(theConnection->m_request);
  206. // insert the header we parsed previously
  207. // into the header map
  208. if (!theConnection->m_currentHeaderField.isEmpty() &&
  209. !theConnection->m_currentHeaderValue.isEmpty()) {
  210. // header names are always lower-cased
  211. theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] =
  212. theConnection->m_currentHeaderValue;
  213. // clear header value. this sets up a nice
  214. // feedback loop where the next time
  215. // HeaderValue is called, it can simply append
  216. theConnection->m_currentHeaderField = QString();
  217. theConnection->m_currentHeaderValue = QString();
  218. }
  219. QString fieldSuffix = QString::fromLatin1(at, length);
  220. theConnection->m_currentHeaderField += fieldSuffix;
  221. return 0;
  222. }
  223. int QHttpConnection::HeaderValue(http_parser *parser, const char *at, size_t length)
  224. {
  225. QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
  226. Q_ASSERT(theConnection->m_request);
  227. QString valueSuffix = QString::fromLatin1(at, length);
  228. theConnection->m_currentHeaderValue += valueSuffix;
  229. return 0;
  230. }
  231. int QHttpConnection::Body(http_parser *parser, const char *at, size_t length)
  232. {
  233. QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
  234. Q_ASSERT(theConnection->m_request);
  235. Q_EMIT theConnection->m_request->data(QByteArray(at, length));
  236. return 0;
  237. }
  238. /// @endcond