qhttpclient.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. #include "private/qhttpclient_private.hpp"
  2. #include <QTimerEvent>
  3. ///////////////////////////////////////////////////////////////////////////////
  4. namespace qhttp {
  5. namespace client {
  6. ///////////////////////////////////////////////////////////////////////////////
  7. QHttpClient::QHttpClient(QObject *parent)
  8. : QObject(parent), d_ptr(new QHttpClientPrivate(this)) {
  9. QHTTP_LINE_LOG
  10. }
  11. QHttpClient::QHttpClient(QHttpClientPrivate &dd, QObject *parent)
  12. : QObject(parent), d_ptr(&dd) {
  13. QHTTP_LINE_LOG
  14. }
  15. QHttpClient::~QHttpClient() {
  16. QHTTP_LINE_LOG
  17. }
  18. quint32
  19. QHttpClient::timeOut() const {
  20. return d_func()->itimeOut;
  21. }
  22. void
  23. QHttpClient::setTimeOut(quint32 t) {
  24. d_func()->itimeOut = t;
  25. }
  26. bool
  27. QHttpClient::isOpen() const {
  28. return d_func()->isocket.isOpen();
  29. }
  30. void
  31. QHttpClient::killConnection() {
  32. Q_D(QHttpClient);
  33. d->iconnectingTimer.stop();
  34. d->itimer.stop();
  35. d->isocket.close();
  36. }
  37. TBackend
  38. QHttpClient::backendType() const {
  39. return d_func()->isocket.ibackendType;
  40. }
  41. QTcpSocket*
  42. QHttpClient::tcpSocket() const {
  43. return d_func()->isocket.itcpSocket;
  44. }
  45. QLocalSocket*
  46. QHttpClient::localSocket() const {
  47. return d_func()->isocket.ilocalSocket;
  48. }
  49. void
  50. QHttpClient::setConnectingTimeOut(quint32 timeout) {
  51. Q_D(QHttpClient);
  52. if ( timeout == 0 ) {
  53. d->iconnectingTimer.stop();
  54. } else {
  55. d->iconnectingTimer.start(timeout,
  56. Qt::CoarseTimer,
  57. this
  58. );
  59. }
  60. }
  61. bool
  62. QHttpClient::request(THttpMethod method, QUrl url,
  63. const TRequstHandler &reqHandler,
  64. const TResponseHandler &resHandler) {
  65. Q_D(QHttpClient);
  66. d->ireqHandler = nullptr;
  67. d->irespHandler = nullptr;
  68. // if url is a local file (UNIX socket) the host could be empty!
  69. if ( !url.isValid() || url.isEmpty() /*|| url.host().isEmpty()*/ )
  70. return false;
  71. // process handlers
  72. if ( resHandler ) {
  73. d->irespHandler = resHandler;
  74. if ( reqHandler )
  75. d->ireqHandler = reqHandler;
  76. else
  77. d->ireqHandler = [](QHttpRequest* req) ->void {
  78. req->addHeader("connection", "close");
  79. req->end();
  80. };
  81. }
  82. auto requestCreator = [this, method, url]() {
  83. // create request object
  84. if ( d_ptr->ilastRequest )
  85. d_ptr->ilastRequest->deleteLater();
  86. d_ptr->ilastRequest = new QHttpRequest(this);
  87. QObject::connect(d_ptr->ilastRequest, &QHttpRequest::done, [this](bool wasTheLastPacket){
  88. d_ptr->ikeepAlive = !wasTheLastPacket;
  89. });
  90. d_ptr->ilastRequest->d_ptr->imethod = method;
  91. d_ptr->ilastRequest->d_ptr->iurl = url;
  92. };
  93. // connecting to host/server must be the last thing. (after all function handlers and ...)
  94. // check for type
  95. if ( url.scheme().toLower() == QLatin1String("file") ) {
  96. d->isocket.ibackendType = ELocalSocket;
  97. d->initializeSocket();
  98. requestCreator();
  99. if ( d->isocket.isOpen() )
  100. d->onConnected();
  101. else
  102. d->isocket.connectTo(url);
  103. } else {
  104. d->isocket.ibackendType = ETcpSocket;
  105. d->initializeSocket();
  106. requestCreator();
  107. if ( d->isocket.isOpen() )
  108. d->onConnected();
  109. else
  110. d->isocket.connectTo(url.host(), url.port(80));
  111. }
  112. return true;
  113. }
  114. void
  115. QHttpClient::timerEvent(QTimerEvent *e) {
  116. Q_D(QHttpClient);
  117. if ( e->timerId() == d->itimer.timerId() ) {
  118. killConnection();
  119. } else if ( e->timerId() == d->iconnectingTimer.timerId() ) {
  120. d->iconnectingTimer.stop();
  121. emit connectingTimeOut();
  122. }
  123. }
  124. void
  125. QHttpClient::onRequestReady(QHttpRequest *req) {
  126. emit httpConnected(req);
  127. }
  128. void
  129. QHttpClient::onResponseReady(QHttpResponse *res) {
  130. emit newResponse(res);
  131. }
  132. ///////////////////////////////////////////////////////////////////////////////
  133. // if user closes the connection, ends the response or by any other reason
  134. // the socket be disconnected, then the iresponse instance may has been deleted.
  135. // In these situations reading more http body or emitting end() for incoming response
  136. // is not possible.
  137. #define CHECK_FOR_DISCONNECTED if ( ilastResponse == nullptr ) \
  138. return 0;
  139. int
  140. QHttpClientPrivate::messageBegin(http_parser*) {
  141. itempHeaderField.clear();
  142. itempHeaderValue.clear();
  143. return 0;
  144. }
  145. int
  146. QHttpClientPrivate::status(http_parser* parser, const char* at, size_t length) {
  147. if ( ilastResponse )
  148. ilastResponse->deleteLater();
  149. ilastResponse = new QHttpResponse(q_func());
  150. ilastResponse->d_func()->istatus = static_cast<TStatusCode>(parser->status_code);
  151. ilastResponse->d_func()->iversion = QString("%1.%2")
  152. .arg(parser->http_major)
  153. .arg(parser->http_minor);
  154. ilastResponse->d_func()->icustomStatusMessage = QString::fromUtf8(at, length);
  155. return 0;
  156. }
  157. int
  158. QHttpClientPrivate::headerField(http_parser*, const char* at, size_t length) {
  159. CHECK_FOR_DISCONNECTED
  160. // insert the header we parsed previously
  161. // into the header map
  162. if ( !itempHeaderField.isEmpty() && !itempHeaderValue.isEmpty() ) {
  163. // header names are always lower-cased
  164. ilastResponse->d_func()->iheaders.insert(
  165. itempHeaderField.toLower(),
  166. itempHeaderValue.toLower()
  167. );
  168. // clear header value. this sets up a nice
  169. // feedback loop where the next time
  170. // HeaderValue is called, it can simply append
  171. itempHeaderField.clear();
  172. itempHeaderValue.clear();
  173. }
  174. itempHeaderField.append(at, length);
  175. return 0;
  176. }
  177. int
  178. QHttpClientPrivate::headerValue(http_parser*, const char* at, size_t length) {
  179. itempHeaderValue.append(at, length);
  180. return 0;
  181. }
  182. int
  183. QHttpClientPrivate::headersComplete(http_parser*) {
  184. CHECK_FOR_DISCONNECTED
  185. // Insert last remaining header
  186. ilastResponse->d_func()->iheaders.insert(
  187. itempHeaderField.toLower(),
  188. itempHeaderValue.toLower()
  189. );
  190. if ( irespHandler )
  191. irespHandler(ilastResponse);
  192. else
  193. q_func()->onResponseReady(ilastResponse);
  194. return 0;
  195. }
  196. int
  197. QHttpClientPrivate::body(http_parser*, const char* at, size_t length) {
  198. CHECK_FOR_DISCONNECTED
  199. ilastResponse->d_func()->ireadState = QHttpResponsePrivate::EPartial;
  200. if ( ilastResponse->d_func()->shouldCollect() ) {
  201. if ( !ilastResponse->d_func()->append(at, length) )
  202. onDispatchResponse(); // forcefully dispatch the ilastResponse
  203. return 0;
  204. }
  205. emit ilastResponse->data(QByteArray(at, length));
  206. return 0;
  207. }
  208. int
  209. QHttpClientPrivate::messageComplete(http_parser*) {
  210. CHECK_FOR_DISCONNECTED
  211. // response is ready to be dispatched
  212. ilastResponse->d_func()->isuccessful = true;
  213. ilastResponse->d_func()->ireadState = QHttpResponsePrivate::EComplete;
  214. return 0;
  215. }
  216. ///////////////////////////////////////////////////////////////////////////////
  217. } // namespace client
  218. } // namespace qhttp
  219. ///////////////////////////////////////////////////////////////////////////////