qhttpbase.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. /** base classes for private implementations.
  2. * https://github.com/azadkuh/qhttp
  3. *
  4. * @author amir zamani
  5. * @version 2.0.0
  6. * @date 2014-07-11
  7. */
  8. #ifndef QHTTPBASE_HPP
  9. #define QHTTPBASE_HPP
  10. #include "qhttpfwd.hpp"
  11. #include <QTcpSocket>
  12. #include <QLocalSocket>
  13. #include <QHostAddress>
  14. #include <QUrl>
  15. #include <QBasicTimer>
  16. #include "http-parser/http_parser.h"
  17. ///////////////////////////////////////////////////////////////////////////////
  18. namespace qhttp {
  19. ///////////////////////////////////////////////////////////////////////////////
  20. class QSocket
  21. {
  22. public:
  23. void close() {
  24. if ( itcpSocket )
  25. itcpSocket->close();
  26. if ( ilocalSocket )
  27. ilocalSocket->close();
  28. }
  29. void release() {
  30. close();
  31. if ( itcpSocket )
  32. itcpSocket->deleteLater();
  33. if ( ilocalSocket )
  34. ilocalSocket->deleteLater();
  35. itcpSocket = nullptr;
  36. ilocalSocket = nullptr;
  37. }
  38. void flush() {
  39. if ( itcpSocket )
  40. itcpSocket->flush();
  41. else if ( ilocalSocket )
  42. ilocalSocket->flush();
  43. }
  44. bool isOpen() const {
  45. if ( ibackendType == ETcpSocket && itcpSocket )
  46. return itcpSocket->isOpen() && itcpSocket->state() == QTcpSocket::ConnectedState;
  47. else if ( ibackendType == ELocalSocket && ilocalSocket )
  48. return ilocalSocket->isOpen() && ilocalSocket->state() == QLocalSocket::ConnectedState;
  49. return false;
  50. }
  51. void connectTo(const QUrl& url) {
  52. if ( ilocalSocket )
  53. ilocalSocket->connectToServer(url.path());
  54. }
  55. void connectTo(const QString& host, quint16 port) {
  56. if ( itcpSocket )
  57. itcpSocket->connectToHost(host, port);
  58. }
  59. qint64 readRaw(char* buffer, int maxlen) {
  60. if ( itcpSocket )
  61. return itcpSocket->read(buffer, maxlen);
  62. else if ( ilocalSocket )
  63. return ilocalSocket->read(buffer, maxlen);
  64. return 0;
  65. }
  66. void writeRaw(const QByteArray& data) {
  67. if ( itcpSocket )
  68. itcpSocket->write(data);
  69. else if ( ilocalSocket )
  70. ilocalSocket->write(data);
  71. }
  72. qint64 bytesAvailable() {
  73. if ( itcpSocket )
  74. return itcpSocket->bytesAvailable();
  75. else if ( ilocalSocket )
  76. return ilocalSocket->bytesAvailable();
  77. return 0;
  78. }
  79. void disconnectAllQtConnections() {
  80. if ( itcpSocket )
  81. QObject::disconnect(itcpSocket, 0, 0, 0);
  82. if ( ilocalSocket )
  83. QObject::disconnect(ilocalSocket, 0, 0, 0);
  84. }
  85. public:
  86. TBackend ibackendType = ETcpSocket;
  87. QTcpSocket* itcpSocket = nullptr;
  88. QLocalSocket* ilocalSocket = nullptr;
  89. };
  90. ///////////////////////////////////////////////////////////////////////////////
  91. class HttpBase
  92. {
  93. public:
  94. THeaderHash iheaders;
  95. QString iversion;
  96. };
  97. ///////////////////////////////////////////////////////////////////////////////
  98. class HttpRequestBase : public HttpBase
  99. {
  100. public:
  101. QUrl iurl;
  102. THttpMethod imethod;
  103. };
  104. ///////////////////////////////////////////////////////////////////////////////
  105. class HttpResponseBase : public HttpBase
  106. {
  107. public:
  108. HttpResponseBase() {
  109. iversion = "1.1";
  110. }
  111. public:
  112. TStatusCode istatus = ESTATUS_BAD_REQUEST;
  113. };
  114. ///////////////////////////////////////////////////////////////////////////////
  115. // usage in client::QHttpResponse, server::QHttpRequest
  116. template<class TBase>
  117. class HttpReader : public TBase
  118. {
  119. public:
  120. enum TReadState {
  121. EEmpty,
  122. EPartial,
  123. EComplete,
  124. ESent
  125. };
  126. public:
  127. void collectData(int atMost) {
  128. icollectCapacity = atMost;
  129. icollectedData.clear();
  130. icollectedData.reserve(atMost);
  131. }
  132. bool shouldCollect() const {
  133. return icollectCapacity > 0;
  134. }
  135. bool append(const char* data, size_t length) {
  136. int currentLength = icollectedData.length();
  137. if ( (currentLength + (int)length) >= icollectCapacity )
  138. return false; // capacity if full
  139. icollectedData.append(data, length);
  140. return true;
  141. }
  142. public:
  143. TReadState ireadState = EEmpty;
  144. bool isuccessful = false;
  145. int icollectCapacity = 0;
  146. QByteArray icollectedData;
  147. };
  148. ///////////////////////////////////////////////////////////////////////////////
  149. // usage in client::QHttpRequest, server::QHttpResponse
  150. template<class TBase, class TImpl>
  151. class HttpWriter : public TBase
  152. {
  153. public:
  154. bool addHeader(const QByteArray &field, const QByteArray &value) {
  155. if ( ifinished )
  156. return false;
  157. TBase::iheaders.insert(field.toLower(), value);
  158. return true;
  159. }
  160. bool writeHeader(const QByteArray& field, const QByteArray& value) {
  161. if ( ifinished )
  162. return false;
  163. QByteArray buffer = QByteArray(field)
  164. .append(": ")
  165. .append(value)
  166. .append("\r\n");
  167. isocket.writeRaw(buffer);
  168. return true;
  169. }
  170. bool writeData(const QByteArray& data) {
  171. if ( ifinished )
  172. return false;
  173. ensureWritingHeaders();
  174. isocket.writeRaw(data);
  175. return true;
  176. }
  177. bool endPacket(const QByteArray& data) {
  178. if ( !writeData(data) )
  179. return false;
  180. isocket.flush();
  181. ifinished = true;
  182. return true;
  183. }
  184. void ensureWritingHeaders() {
  185. if ( ifinished || iheaderWritten )
  186. return;
  187. TImpl* me = static_cast<TImpl*>(this);
  188. isocket.writeRaw(me->makeTitle());
  189. writeHeaders();
  190. iheaderWritten = true;
  191. }
  192. void writeHeaders(bool doFlush = false) {
  193. if ( ifinished || iheaderWritten )
  194. return;
  195. if ( TBase::iheaders.keyHasValue("connection", "keep-alive") )
  196. ikeepAlive = true;
  197. else
  198. TBase::iheaders.insert("connection", "close");
  199. TImpl* me = static_cast<TImpl*>(this);
  200. me->prepareHeadersToWrite();
  201. for ( auto cit = TBase::iheaders.constBegin(); cit != TBase::iheaders.constEnd(); cit++ ) {
  202. const QByteArray& field = cit.key();
  203. const QByteArray& value = cit.value();
  204. writeHeader(field, value);
  205. }
  206. isocket.writeRaw("\r\n");
  207. if ( doFlush )
  208. isocket.flush();
  209. }
  210. public:
  211. QSocket isocket;
  212. bool ifinished = false;
  213. bool iheaderWritten = false;
  214. bool ikeepAlive = false;
  215. };
  216. ///////////////////////////////////////////////////////////////////////////////
  217. // usage in client::QHttpClient, server::QHttpConnection
  218. template<class TImpl>
  219. class HttpParser
  220. {
  221. public:
  222. explicit HttpParser(http_parser_type type) {
  223. // create http_parser object
  224. iparser.data = static_cast<TImpl*>(this);
  225. http_parser_init(&iparser, type);
  226. memset(&iparserSettings, 0, sizeof(http_parser_settings));
  227. iparserSettings.on_message_begin = onMessageBegin;
  228. iparserSettings.on_url = onUrl;
  229. iparserSettings.on_status = onStatus;
  230. iparserSettings.on_header_field = onHeaderField;
  231. iparserSettings.on_header_value = onHeaderValue;
  232. iparserSettings.on_headers_complete = onHeadersComplete;
  233. iparserSettings.on_body = onBody;
  234. iparserSettings.on_message_complete = onMessageComplete;
  235. }
  236. size_t parse(const char* data, size_t length) {
  237. return http_parser_execute(&iparser,
  238. &iparserSettings,
  239. data,
  240. length);
  241. }
  242. public: // callback functions for http_parser_settings
  243. static int onMessageBegin(http_parser* parser) {
  244. TImpl *me = static_cast<TImpl*>(parser->data);
  245. return me->messageBegin(parser);
  246. }
  247. static int onUrl(http_parser* parser, const char* at, size_t length) {
  248. TImpl *me = static_cast<TImpl*>(parser->data);
  249. return me->url(parser, at, length);
  250. }
  251. static int onStatus(http_parser* parser, const char* at, size_t length) {
  252. TImpl *me = static_cast<TImpl*>(parser->data);
  253. return me->status(parser, at, length);
  254. }
  255. static int onHeaderField(http_parser* parser, const char* at, size_t length) {
  256. TImpl *me = static_cast<TImpl*>(parser->data);
  257. return me->headerField(parser, at, length);
  258. }
  259. static int onHeaderValue(http_parser* parser, const char* at, size_t length) {
  260. TImpl *me = static_cast<TImpl*>(parser->data);
  261. return me->headerValue(parser, at, length);
  262. }
  263. static int onHeadersComplete(http_parser* parser) {
  264. TImpl *me = static_cast<TImpl*>(parser->data);
  265. return me->headersComplete(parser);
  266. }
  267. static int onBody(http_parser* parser, const char* at, size_t length) {
  268. TImpl *me = static_cast<TImpl*>(parser->data);
  269. return me->body(parser, at, length);
  270. }
  271. static int onMessageComplete(http_parser* parser) {
  272. TImpl *me = static_cast<TImpl*>(parser->data);
  273. return me->messageComplete(parser);
  274. }
  275. protected:
  276. // The ones we are reading in from the parser
  277. QByteArray itempHeaderField;
  278. QByteArray itempHeaderValue;
  279. // if connection has a timeout, these fields will be used
  280. quint32 itimeOut = 0;
  281. QBasicTimer itimer;
  282. // uniform socket object
  283. QSocket isocket;
  284. // if connection should persist
  285. bool ikeepAlive = false;
  286. protected:
  287. http_parser iparser;
  288. http_parser_settings iparserSettings;
  289. };
  290. ///////////////////////////////////////////////////////////////////////////////
  291. #if 0
  292. template<class T>
  293. class HttpWriterBase
  294. {
  295. public:
  296. explicit HttpWriterBase() {
  297. }
  298. virtual ~HttpWriterBase() {
  299. }
  300. void initialize() {
  301. reset();
  302. if ( itcpSocket ) {
  303. // first disconnects previous dangling lambdas
  304. QObject::disconnect(itcpSocket, &QTcpSocket::bytesWritten, 0, 0);
  305. QObject::connect(itcpSocket, &QTcpSocket::bytesWritten, [this](qint64 ){
  306. if ( itcpSocket->bytesToWrite() == 0 )
  307. static_cast<T*>(this)->allBytesWritten();
  308. });
  309. } else if ( ilocalSocket ) {
  310. // first disconnects previous dangling lambdas
  311. QObject::disconnect(ilocalSocket, &QLocalSocket::bytesWritten, 0, 0);
  312. QObject::connect(ilocalSocket, &QLocalSocket::bytesWritten, [this](qint64 ){
  313. if ( ilocalSocket->bytesToWrite() == 0 )
  314. static_cast<T*>(this)->allBytesWritten();
  315. });
  316. }
  317. }
  318. void reset() {
  319. iheaderWritten = false;
  320. ifinished = false;
  321. }
  322. public:
  323. bool addHeader(const QByteArray &field, const QByteArray &value) {
  324. if ( ifinished )
  325. return false;
  326. static_cast<T*>(this)->iheaders.insert(field.toLower(), value);
  327. return true;
  328. }
  329. bool writeHeader(const QByteArray& field, const QByteArray& value) {
  330. if ( ifinished )
  331. return false;
  332. QByteArray buffer = QByteArray(field)
  333. .append(": ")
  334. .append(value)
  335. .append("\r\n");
  336. writeRaw(buffer);
  337. return true;
  338. }
  339. bool writeData(const QByteArray& data) {
  340. if ( ifinished )
  341. return false;
  342. static_cast<T*>(this)->ensureWritingHeaders();
  343. writeRaw(data);
  344. return true;
  345. }
  346. bool endPacket(const QByteArray& data) {
  347. if ( !writeData(data) )
  348. return false;
  349. if ( itcpSocket )
  350. itcpSocket->flush();
  351. else if ( ilocalSocket )
  352. ilocalSocket->flush();
  353. ifinished = true;
  354. return true;
  355. }
  356. protected:
  357. void writeRaw(const QByteArray &data) {
  358. if ( itcpSocket )
  359. itcpSocket->write(data);
  360. else if ( ilocalSocket )
  361. ilocalSocket->write(data);
  362. }
  363. public:
  364. QTcpSocket* itcpSocket = nullptr;
  365. QLocalSocket* ilocalSocket = nullptr;
  366. bool iheaderWritten;
  367. bool ifinished;
  368. };
  369. #endif
  370. ///////////////////////////////////////////////////////////////////////////////
  371. } // namespace qhttp
  372. ///////////////////////////////////////////////////////////////////////////////
  373. #endif // QHTTPBASE_HPP