qhttpclient.cpp 6.6 KB

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