qhttpserverconnection.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. #include "private/qhttpserverconnection_private.hpp"
  2. ///////////////////////////////////////////////////////////////////////////////
  3. namespace qhttp {
  4. namespace server {
  5. ///////////////////////////////////////////////////////////////////////////////
  6. QHttpConnection::QHttpConnection(QObject *parent)
  7. : QObject(parent), d_ptr(new QHttpConnectionPrivate(this)) {
  8. QHTTP_LINE_LOG
  9. }
  10. QHttpConnection::QHttpConnection(QHttpConnectionPrivate& dd, QObject* parent)
  11. : QObject(parent), d_ptr(&dd) {
  12. QHTTP_LINE_LOG
  13. }
  14. void
  15. QHttpConnection::setSocketDescriptor(qintptr sokDescriptor, TBackend backendType) {
  16. d_ptr->createSocket(sokDescriptor, backendType);
  17. }
  18. QHttpConnection::~QHttpConnection() {
  19. QHTTP_LINE_LOG
  20. }
  21. void
  22. QHttpConnection::setTimeOut(quint32 miliSeconds) {
  23. if ( miliSeconds != 0 ) {
  24. d_func()->itimeOut = miliSeconds;
  25. d_func()->itimer.start(miliSeconds, Qt::CoarseTimer, this);
  26. }
  27. }
  28. void
  29. QHttpConnection::killConnection() {
  30. d_func()->isocket.close();
  31. }
  32. TBackend
  33. QHttpConnection::backendType() const {
  34. return d_func()->isocket.ibackendType;
  35. }
  36. QTcpSocket*
  37. QHttpConnection::tcpSocket() const {
  38. return d_func()->isocket.itcpSocket;
  39. }
  40. QLocalSocket*
  41. QHttpConnection::localSocket() const {
  42. return d_func()->isocket.ilocalSocket;
  43. }
  44. void
  45. QHttpConnection::onHandler(const TServerHandler &handler) {
  46. d_func()->ihandler = handler;
  47. }
  48. void
  49. QHttpConnection::timerEvent(QTimerEvent *) {
  50. killConnection();
  51. }
  52. ///////////////////////////////////////////////////////////////////////////////
  53. // if user closes the connection, ends the response or by any other reason
  54. // the socket be disconnected, then the irequest and iresponse instances may bhave been deleted.
  55. // In these situations reading more http body or emitting end() for incoming request
  56. // are not possible.
  57. #define CHECK_FOR_DISCONNECTED if ( ilastRequest == nullptr ) \
  58. return 0;
  59. int
  60. QHttpConnectionPrivate::messageBegin(http_parser*) {
  61. itempUrl.clear();
  62. itempUrl.reserve(128);
  63. if ( ilastRequest )
  64. ilastRequest->deleteLater();
  65. ilastRequest = new QHttpRequest(q_func());
  66. return 0;
  67. }
  68. int
  69. QHttpConnectionPrivate::url(http_parser*, const char* at, size_t length) {
  70. Q_ASSERT(ilastRequest);
  71. itempUrl.append(at, length);
  72. return 0;
  73. }
  74. int
  75. QHttpConnectionPrivate::headerField(http_parser*, const char* at, size_t length) {
  76. CHECK_FOR_DISCONNECTED
  77. // insert the header we parsed previously
  78. // into the header map
  79. if ( !itempHeaderField.isEmpty() && !itempHeaderValue.isEmpty() ) {
  80. // header names are always lower-cased
  81. ilastRequest->d_func()->iheaders.insert(
  82. itempHeaderField.toLower(),
  83. itempHeaderValue.toLower()
  84. );
  85. // clear header value. this sets up a nice
  86. // feedback loop where the next time
  87. // HeaderValue is called, it can simply append
  88. itempHeaderField.clear();
  89. itempHeaderValue.clear();
  90. }
  91. itempHeaderField.append(at, length);
  92. return 0;
  93. }
  94. int
  95. QHttpConnectionPrivate::headerValue(http_parser*, const char* at, size_t length) {
  96. CHECK_FOR_DISCONNECTED
  97. itempHeaderValue.append(at, length);
  98. return 0;
  99. }
  100. int
  101. QHttpConnectionPrivate::headersComplete(http_parser* parser) {
  102. CHECK_FOR_DISCONNECTED
  103. #if defined(USE_CUSTOM_URL_CREATOR)
  104. // get parsed url
  105. struct http_parser_url urlInfo;
  106. int r = http_parser_parse_url(itempUrl.constData(),
  107. itempUrl.size(),
  108. parser->method == HTTP_CONNECT,
  109. &urlInfo);
  110. Q_ASSERT(r == 0);
  111. Q_UNUSED(r);
  112. ilastRequest->d_func()->iurl = createUrl(
  113. itempUrl.constData(),
  114. urlInfo
  115. );
  116. #else
  117. ilastRequest->d_func()->iurl = QUrl(itempUrl);
  118. #endif // defined(USE_CUSTOM_URL_CREATOR)
  119. // set method
  120. ilastRequest->d_func()->imethod =
  121. static_cast<THttpMethod>(parser->method);
  122. // set version
  123. ilastRequest->d_func()->iversion = QString("%1.%2")
  124. .arg(parser->http_major)
  125. .arg(parser->http_minor);
  126. // Insert last remaining header
  127. ilastRequest->d_func()->iheaders.insert(
  128. itempHeaderField.toLower(),
  129. itempHeaderValue.toLower()
  130. );
  131. // set client information
  132. if ( isocket.ibackendType == ETcpSocket ) {
  133. ilastRequest->d_func()->iremoteAddress = isocket.itcpSocket->peerAddress().toString();
  134. ilastRequest->d_func()->iremotePort = isocket.itcpSocket->peerPort();
  135. } else if ( isocket.ibackendType == ELocalSocket ) {
  136. ilastRequest->d_func()->iremoteAddress = isocket.ilocalSocket->fullServerName();
  137. ilastRequest->d_func()->iremotePort = 0; // not used in local sockets
  138. }
  139. if ( ilastResponse )
  140. ilastResponse->deleteLater();
  141. ilastResponse = new QHttpResponse(q_func());
  142. if ( parser->http_major < 1 || parser->http_minor < 1 )
  143. ilastResponse->d_func()->ikeepAlive = false;
  144. // close the connection if response was the last packet
  145. QObject::connect(ilastResponse, &QHttpResponse::done, [this](bool wasTheLastPacket){
  146. ikeepAlive = !wasTheLastPacket;
  147. if ( wasTheLastPacket ) {
  148. isocket.flush();
  149. isocket.close();
  150. }
  151. });
  152. // we are good to go!
  153. if ( ihandler )
  154. ihandler(ilastRequest, ilastResponse);
  155. else
  156. emit q_ptr->newRequest(ilastRequest, ilastResponse);
  157. return 0;
  158. }
  159. int
  160. QHttpConnectionPrivate::body(http_parser*, const char* at, size_t length) {
  161. CHECK_FOR_DISCONNECTED
  162. ilastRequest->d_func()->ireadState = QHttpRequestPrivate::EPartial;
  163. if ( ilastRequest->d_func()->shouldCollect() ) {
  164. if ( !ilastRequest->d_func()->append(at, length) )
  165. onDispatchRequest(); // forcefully dispatch the ilastRequest
  166. return 0;
  167. }
  168. emit ilastRequest->data(QByteArray(at, length));
  169. return 0;
  170. }
  171. int
  172. QHttpConnectionPrivate::messageComplete(http_parser*) {
  173. CHECK_FOR_DISCONNECTED
  174. // request is ready to be dispatched
  175. ilastRequest->d_func()->isuccessful = true;
  176. ilastRequest->d_func()->ireadState = QHttpRequestPrivate::EComplete;
  177. return 0;
  178. }
  179. ///////////////////////////////////////////////////////////////////////////////
  180. #if defined(USE_CUSTOM_URL_CREATOR)
  181. ///////////////////////////////////////////////////////////////////////////////
  182. /* URL Utilities */
  183. #define HAS_URL_FIELD(info, field) (info.field_set &(1 << (field)))
  184. #define GET_FIELD(data, info, field) \
  185. QString::fromLatin1(data + info.field_data[field].off, info.field_data[field].len)
  186. #define CHECK_AND_GET_FIELD(data, info, field) \
  187. (HAS_URL_FIELD(info, field) ? GET_FIELD(data, info, field) : QString())
  188. QUrl
  189. QHttpConnectionPrivate::createUrl(const char *urlData, const http_parser_url &urlInfo) {
  190. QUrl url;
  191. url.setScheme(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_SCHEMA));
  192. url.setHost(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_HOST));
  193. // Port is dealt with separately since it is available as an integer.
  194. url.setPath(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_PATH));
  195. #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
  196. url.setQuery(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_QUERY));
  197. #else
  198. if (HAS_URL_FIELD(urlInfo, UF_QUERY)) {
  199. url.setEncodedQuery(QByteArray(urlData + urlInfo.field_data[UF_QUERY].off,
  200. urlInfo.field_data[UF_QUERY].len));
  201. }
  202. #endif
  203. url.setFragment(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_FRAGMENT));
  204. url.setUserInfo(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_USERINFO));
  205. if (HAS_URL_FIELD(urlInfo, UF_PORT))
  206. url.setPort(urlInfo.port);
  207. return url;
  208. }
  209. #undef CHECK_AND_SET_FIELD
  210. #undef GET_FIELD
  211. #undef HAS_URL_FIELD
  212. ///////////////////////////////////////////////////////////////////////////////
  213. #endif // defined(USE_CUSTOM_URL_CREATOR)
  214. ///////////////////////////////////////////////////////////////////////////////
  215. } // namespace server
  216. } // namespace qhttp
  217. ///////////////////////////////////////////////////////////////////////////////