JQHttpServer.cpp 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154
  1. /*
  2. This file is part of JQLibrary
  3. Copyright: Jason
  4. Contact email: 188080501@qq.com
  5. GNU Lesser General Public License Usage
  6. Alternatively, this file may be used under the terms of the GNU Lesser
  7. General Public License version 2.1 or version 3 as published by the Free
  8. Software Foundation and appearing in the file LICENSE.LGPLv21 and
  9. LICENSE.LGPLv3 included in the packaging of this file. Please review the
  10. following information to ensure the GNU Lesser General Public License
  11. requirements will be met: https://www.gnu.org/licenses/lgpl.html and
  12. http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  13. */
  14. #include "JQHttpServer.h"
  15. // Qt lib import
  16. #include <QEventLoop>
  17. #include <QTimer>
  18. #include <QSemaphore>
  19. #include <QMetaObject>
  20. #include <QThread>
  21. #include <QThreadPool>
  22. #include <QJsonDocument>
  23. #include <QJsonValue>
  24. #include <QPointer>
  25. #include <QFile>
  26. #include <QImage>
  27. #include <QBuffer>
  28. #include <QtConcurrent>
  29. #include <QTcpServer>
  30. #include <QTcpSocket>
  31. #include <QLocalServer>
  32. #include <QLocalSocket>
  33. #ifndef QT_NO_SSL
  34. # include <QSslSocket>
  35. # include <QSslKey>
  36. # include <QSslCertificate>
  37. # include <QSslConfiguration>
  38. #endif
  39. #define JQHTTPSERVER_SESSION_PROTECTION( functionName, ... ) \
  40. auto this_ = this; \
  41. if ( !this_ || ( contentLength_ < -1 ) || ( waitWrittenByteCount_ < -1 ) ) \
  42. { \
  43. qDebug() << QStringLiteral( "JQHttpServer::Session::" ) + functionName + ": current session this is null"; \
  44. return __VA_ARGS__; \
  45. }
  46. static QString replyTextFormat(
  47. "HTTP/1.1 %1 OK\r\n"
  48. "Content-Type: %2\r\n"
  49. "Content-Length: %3\r\n"
  50. "Access-Control-Allow-Origin: *\r\n"
  51. "\r\n"
  52. "%4"
  53. );
  54. static QString replyRedirectsFormat(
  55. "HTTP/1.1 %1 OK\r\n"
  56. "Content-Type: %2\r\n"
  57. "Content-Length: %3\r\n"
  58. "Access-Control-Allow-Origin: *\r\n"
  59. "\r\n"
  60. "%4"
  61. );
  62. static QString replyFileFormat(
  63. "HTTP/1.1 %1 OK\r\n"
  64. "Content-Disposition: attachment;filename=%2\r\n"
  65. "Content-Length: %3\r\n"
  66. "Access-Control-Allow-Origin: *\r\n"
  67. "\r\n"
  68. );
  69. static QString replyImageFormat(
  70. "HTTP/1.1 %1\r\n"
  71. "Content-Type: image\r\n"
  72. "Content-Length: %2\r\n"
  73. "Access-Control-Allow-Origin: *\r\n"
  74. "\r\n"
  75. );
  76. static QString replyBytesFormat(
  77. "HTTP/1.1 %1 OK\r\n"
  78. "Content-Type: application/octet-stream\r\n"
  79. "Content-Length: %2\r\n"
  80. "Access-Control-Allow-Origin: *\r\n"
  81. "\r\n"
  82. );
  83. static QString replyOptionsFormat(
  84. "HTTP/1.1 200 OK\r\n"
  85. "Allow: OPTIONS, GET, POST, PUT, HEAD\r\n"
  86. "Access-Control-Allow-Methods: OPTIONS, GET, POST, PUT, HEAD\r\n"
  87. "Content-Length: 0\r\n"
  88. "Access-Control-Allow-Origin: *\r\n"
  89. "\r\n"
  90. );
  91. // Session
  92. JQHttpServer::Session::Session(const QPointer<QIODevice> &tcpSocket):
  93. ioDevice_( tcpSocket ),
  94. timerForClose_( new QTimer )
  95. {
  96. timerForClose_->setInterval( 30 * 1000 );
  97. connect( ioDevice_.data(), &QIODevice::readyRead, [ this ]()
  98. {
  99. if ( this->timerForClose_->isActive() )
  100. {
  101. timerForClose_->stop();
  102. }
  103. const auto &&data = this->ioDevice_->readAll();
  104. // qDebug() << data;
  105. this->buffer_.append( data );
  106. this->inspectionBufferSetup1();
  107. timerForClose_->start();
  108. } );
  109. connect( ioDevice_.data(), &QIODevice::bytesWritten, [ this ](const qint64 &bytes)
  110. {
  111. this->waitWrittenByteCount_ -= bytes;
  112. if ( this->waitWrittenByteCount_ == 0 )
  113. {
  114. this->deleteLater();
  115. return;
  116. }
  117. if ( !ioDeviceForReply_.isNull() )
  118. {
  119. if ( ioDeviceForReply_->atEnd() )
  120. {
  121. ioDeviceForReply_->deleteLater();
  122. ioDeviceForReply_.clear();
  123. }
  124. else
  125. {
  126. ioDevice_->write( ioDeviceForReply_->read( 512 * 1024 ) );
  127. }
  128. }
  129. if ( this->timerForClose_->isActive() )
  130. {
  131. timerForClose_->stop();
  132. }
  133. timerForClose_->start();
  134. } );
  135. connect( timerForClose_.data(), &QTimer::timeout, this, &QObject::deleteLater );
  136. }
  137. JQHttpServer::Session::~Session()
  138. {
  139. if ( !ioDevice_.isNull() )
  140. {
  141. delete ioDevice_.data();
  142. }
  143. }
  144. QString JQHttpServer::Session::requestUrlPath() const
  145. {
  146. JQHTTPSERVER_SESSION_PROTECTION( "requestUrlPath", { } );
  147. QString result;
  148. const auto indexForQueryStart = requestUrl_.indexOf( "?" );
  149. if ( indexForQueryStart >= 0 )
  150. {
  151. result = requestUrl_.mid( 0, indexForQueryStart );
  152. }
  153. else
  154. {
  155. result = requestUrl_;
  156. }
  157. if ( result.startsWith( "//" ) )
  158. {
  159. result = result.mid( 1 );
  160. }
  161. if ( result.endsWith( "/" ) )
  162. {
  163. result = result.mid( 0, result.size() - 1 );
  164. }
  165. result.replace( "%5B", "[" );
  166. result.replace( "%5D", "]" );
  167. result.replace( "%7B", "{" );
  168. result.replace( "%7D", "}" );
  169. result.replace( "%5E", "^" );
  170. return result;
  171. }
  172. QStringList JQHttpServer::Session::requestUrlPathSplitToList() const
  173. {
  174. auto list = this->requestUrlPath().split( "/" );
  175. while ( !list.isEmpty() && list.first().isEmpty() )
  176. {
  177. list.pop_front();
  178. }
  179. while ( !list.isEmpty() && list.last().isEmpty() )
  180. {
  181. list.pop_back();
  182. }
  183. return list;
  184. }
  185. QMap< QString, QString > JQHttpServer::Session::requestUrlQuery() const
  186. {
  187. const auto indexForQueryStart = requestUrl_.indexOf( "?" );
  188. if ( indexForQueryStart < 0 ) { return { }; }
  189. QMap< QString, QString > result;
  190. auto lines = QUrl::fromEncoded( requestUrl_.mid( indexForQueryStart + 1 ).toUtf8() ).toString().split( "&" );
  191. for ( const auto &line_: lines )
  192. {
  193. auto line = line_;
  194. line.replace( "%5B", "[" );
  195. line.replace( "%5D", "]" );
  196. line.replace( "%7B", "{" );
  197. line.replace( "%7D", "}" );
  198. line.replace( "%5E", "^" );
  199. auto indexOf = line.indexOf( "=" );
  200. if ( indexOf > 0 )
  201. {
  202. result[ line.mid( 0, indexOf ) ] = line.mid( indexOf + 1 );
  203. }
  204. }
  205. return result;
  206. }
  207. void JQHttpServer::Session::replyText(const QString &replyData, const int &httpStatusCode)
  208. {
  209. JQHTTPSERVER_SESSION_PROTECTION( "replyText" );
  210. if ( alreadyReply_ )
  211. {
  212. qDebug() << "JQHttpServer::Session::replyText: already reply";
  213. return;
  214. }
  215. if ( QThread::currentThread() != this->thread() )
  216. {
  217. QMetaObject::invokeMethod( this, "replyText", Qt::QueuedConnection, Q_ARG( QString, replyData ), Q_ARG( int, httpStatusCode ) );
  218. return;
  219. }
  220. alreadyReply_ = true;
  221. if ( ioDevice_.isNull() )
  222. {
  223. qDebug() << "JQHttpServer::Session::replyText: error1";
  224. this->deleteLater();
  225. return;
  226. }
  227. const auto &&data = replyTextFormat.arg(
  228. QString::number( httpStatusCode ),
  229. "text;charset=UTF-8",
  230. QString::number( replyData.toUtf8().size() ),
  231. replyData
  232. ).toUtf8();
  233. waitWrittenByteCount_ = data.size();
  234. ioDevice_->write( data );
  235. }
  236. void JQHttpServer::Session::replyRedirects(const QUrl &targetUrl, const int &httpStatusCode)
  237. {
  238. JQHTTPSERVER_SESSION_PROTECTION( "replyRedirects" );
  239. if ( alreadyReply_ )
  240. {
  241. qDebug() << "JQHttpServer::Session::replyRedirects: already reply";
  242. return;
  243. }
  244. if ( QThread::currentThread() != this->thread() )
  245. {
  246. QMetaObject::invokeMethod( this, "replyRedirects", Qt::QueuedConnection, Q_ARG( QUrl, targetUrl ), Q_ARG( int, httpStatusCode ) );
  247. return;
  248. }
  249. alreadyReply_ = true;
  250. if ( ioDevice_.isNull() )
  251. {
  252. qDebug() << "JQHttpServer::Session::replyRedirects: error1";
  253. this->deleteLater();
  254. return;
  255. }
  256. const auto &&buffer = QString( "<head>\n<meta http-equiv=\"refresh\" content=\"0;URL=%1/\" />\n</head>" ).arg( targetUrl.toString() );
  257. const auto &&data = replyRedirectsFormat.arg(
  258. QString::number( httpStatusCode ),
  259. "text;charset=UTF-8",
  260. QString::number( buffer.toUtf8().size() ),
  261. buffer
  262. ).toUtf8();
  263. waitWrittenByteCount_ = data.size();
  264. ioDevice_->write( data );
  265. }
  266. void JQHttpServer::Session::replyJsonObject(const QJsonObject &jsonObject, const int &httpStatusCode)
  267. {
  268. JQHTTPSERVER_SESSION_PROTECTION( "replyJsonObject" );
  269. if ( alreadyReply_ )
  270. {
  271. qDebug() << "JQHttpServer::Session::replyJsonObject: already reply";
  272. return;
  273. }
  274. if ( QThread::currentThread() != this->thread() )
  275. {
  276. QMetaObject::invokeMethod( this, "replyJsonObject", Qt::QueuedConnection, Q_ARG( QJsonObject, jsonObject ), Q_ARG( int, httpStatusCode ) );
  277. return;
  278. }
  279. alreadyReply_ = true;
  280. if ( ioDevice_.isNull() )
  281. {
  282. qDebug() << "JQHttpServer::Session::replyJsonObject: error1";
  283. this->deleteLater();
  284. return;
  285. }
  286. const auto &&data = QJsonDocument( jsonObject ).toJson( QJsonDocument::Compact );
  287. const auto &&data2 = replyTextFormat.arg(
  288. QString::number( httpStatusCode ),
  289. "application/json;charset=UTF-8",
  290. QString::number( data.size() ),
  291. QString( data )
  292. ).toUtf8();
  293. waitWrittenByteCount_ = data2.size();
  294. ioDevice_->write( data2 );
  295. }
  296. void JQHttpServer::Session::replyJsonArray(const QJsonArray &jsonArray, const int &httpStatusCode)
  297. {
  298. JQHTTPSERVER_SESSION_PROTECTION( "replyJsonArray" );
  299. if ( alreadyReply_ )
  300. {
  301. qDebug() << "JQHttpServer::Session::replyJsonArray: already reply";
  302. return;
  303. }
  304. if ( QThread::currentThread() != this->thread() )
  305. {
  306. QMetaObject::invokeMethod( this, "replyJsonArray", Qt::QueuedConnection, Q_ARG( QJsonArray, jsonArray ), Q_ARG( int, httpStatusCode ) );
  307. return;
  308. }
  309. alreadyReply_ = true;
  310. if ( ioDevice_.isNull() )
  311. {
  312. qDebug() << "JQHttpServer::Session::replyJsonArray: error1";
  313. this->deleteLater();
  314. return;
  315. }
  316. const auto &&data = QJsonDocument( jsonArray ).toJson( QJsonDocument::Compact );
  317. const auto &&data2 = replyTextFormat.arg(
  318. QString::number( httpStatusCode ),
  319. "application/json;charset=UTF-8",
  320. QString::number( data.size() ),
  321. QString( data )
  322. ).toUtf8();
  323. waitWrittenByteCount_ = data2.size();
  324. ioDevice_->write( data2 );
  325. }
  326. void JQHttpServer::Session::replyFile(const QString &filePath, const int &httpStatusCode)
  327. {
  328. JQHTTPSERVER_SESSION_PROTECTION( "replyFile" );
  329. if ( alreadyReply_ )
  330. {
  331. qDebug() << "JQHttpServer::Session::replyFile: already reply";
  332. return;
  333. }
  334. if ( QThread::currentThread() != this->thread() )
  335. {
  336. QMetaObject::invokeMethod( this, "replyFile", Qt::QueuedConnection, Q_ARG( QString, filePath ), Q_ARG( int, httpStatusCode ) );
  337. return;
  338. }
  339. alreadyReply_ = true;
  340. if ( ioDevice_.isNull() )
  341. {
  342. qDebug() << "JQHttpServer::Session::replyFile: error1";
  343. this->deleteLater();
  344. return;
  345. }
  346. ioDeviceForReply_.reset( new QFile( filePath ) );
  347. QPointer< QFile > file = ( qobject_cast< QFile * >( ioDeviceForReply_.data() ) );
  348. if ( !file->open( QIODevice::ReadOnly ) )
  349. {
  350. qDebug() << "JQHttpServer::Session::replyFile: open file error:" << filePath;
  351. ioDeviceForReply_.clear();
  352. this->deleteLater();
  353. return;
  354. }
  355. const auto &&data = replyFileFormat.arg(
  356. QString::number( httpStatusCode ),
  357. QFileInfo( filePath ).fileName(),
  358. QString::number( file->size() )
  359. ).toUtf8();
  360. waitWrittenByteCount_ = data.size() + file->size();
  361. ioDevice_->write( data );
  362. }
  363. void JQHttpServer::Session::replyImage(const QImage &image, const int &httpStatusCode)
  364. {
  365. JQHTTPSERVER_SESSION_PROTECTION( "replyImage" );
  366. if ( alreadyReply_ )
  367. {
  368. qDebug() << "JQHttpServer::Session::replyImage: already reply";
  369. return;
  370. }
  371. if ( QThread::currentThread() != this->thread() )
  372. {
  373. QMetaObject::invokeMethod( this, "replyImage", Qt::QueuedConnection, Q_ARG( QImage, image ), Q_ARG( int, httpStatusCode ) );
  374. return;
  375. }
  376. alreadyReply_ = true;
  377. if ( ioDevice_.isNull() )
  378. {
  379. qDebug() << "JQHttpServer::Session::replyImage: error1";
  380. this->deleteLater();
  381. return;
  382. }
  383. auto buffer = new QBuffer;
  384. if ( !buffer->open( QIODevice::ReadWrite ) )
  385. {
  386. qDebug() << "JQHttpServer::Session::replyImage: open buffer error";
  387. delete buffer;
  388. this->deleteLater();
  389. return;
  390. }
  391. if ( !image.save( buffer, "PNG" ) )
  392. {
  393. qDebug() << "JQHttpServer::Session::replyImage: save image to buffer error";
  394. delete buffer;
  395. this->deleteLater();
  396. return;
  397. }
  398. ioDeviceForReply_.reset( buffer );
  399. ioDeviceForReply_->seek( 0 );
  400. const auto &&data = replyImageFormat.arg(
  401. QString::number( httpStatusCode ),
  402. QString::number( buffer->buffer().size() )
  403. ).toUtf8();
  404. waitWrittenByteCount_ = data.size() + buffer->buffer().size();
  405. ioDevice_->write( data );
  406. }
  407. void JQHttpServer::Session::replyImage(const QString &imageFilePath, const int &httpStatusCode)
  408. {
  409. JQHTTPSERVER_SESSION_PROTECTION( "replyImage" );
  410. if ( alreadyReply_ )
  411. {
  412. qDebug() << "JQHttpServer::Session::replyImage: already reply";
  413. return;
  414. }
  415. if ( QThread::currentThread() != this->thread() )
  416. {
  417. QMetaObject::invokeMethod( this, "replyImage", Qt::QueuedConnection, Q_ARG( QString, imageFilePath ), Q_ARG( int, httpStatusCode ) );
  418. return;
  419. }
  420. alreadyReply_ = true;
  421. if ( ioDevice_.isNull() )
  422. {
  423. qDebug() << "JQHttpServer::Session::replyImage: error1";
  424. this->deleteLater();
  425. return;
  426. }
  427. auto buffer = new QFile( imageFilePath );
  428. if ( !buffer->open( QIODevice::ReadWrite ) )
  429. {
  430. qDebug() << "JQHttpServer::Session::replyImage: open buffer error";
  431. delete buffer;
  432. this->deleteLater();
  433. return;
  434. }
  435. ioDeviceForReply_.reset( buffer );
  436. ioDeviceForReply_->seek( 0 );
  437. const auto &&data = replyImageFormat.arg(
  438. QString::number( httpStatusCode ),
  439. QString::number( buffer->size() )
  440. ).toUtf8();
  441. waitWrittenByteCount_ = data.size() + buffer->size();
  442. ioDevice_->write( data );
  443. }
  444. void JQHttpServer::Session::replyBytes(const QByteArray &bytes, const int &httpStatusCode)
  445. {
  446. JQHTTPSERVER_SESSION_PROTECTION( "replyBytes" );
  447. if (QThread::currentThread() != this->thread())
  448. {
  449. QMetaObject::invokeMethod(this, "replyBytes", Qt::QueuedConnection, Q_ARG(QByteArray, bytes), Q_ARG(int, httpStatusCode));
  450. return;
  451. }
  452. if (alreadyReply_)
  453. {
  454. qDebug() << "JQHttpServer::Session::replyBytes: already reply";
  455. return;
  456. }
  457. alreadyReply_ = true;
  458. if (ioDevice_.isNull())
  459. {
  460. qDebug() << "JQHttpServer::Session::replyBytes: error1";
  461. this->deleteLater();
  462. return;
  463. }
  464. auto buffer = new QBuffer;
  465. buffer->setData(bytes);
  466. if (!buffer->open(QIODevice::ReadWrite))
  467. {
  468. qDebug() << "JQHttpServer::Session::replyBytes: open buffer error";
  469. delete buffer;
  470. this->deleteLater();
  471. return;
  472. }
  473. ioDeviceForReply_.reset(buffer);
  474. ioDeviceForReply_->seek(0);
  475. const auto &&data = replyBytesFormat.arg(
  476. QString::number(httpStatusCode),
  477. QString::number(buffer->buffer().size())
  478. ).toUtf8();
  479. waitWrittenByteCount_ = data.size() + buffer->buffer().size();
  480. ioDevice_->write(data);
  481. }
  482. void JQHttpServer::Session::replyOptions()
  483. {
  484. JQHTTPSERVER_SESSION_PROTECTION( "replyOptions" );
  485. if ( alreadyReply_ )
  486. {
  487. qDebug() << "JQHttpServer::Session::replyOptions: already reply";
  488. return;
  489. }
  490. if ( QThread::currentThread() != this->thread() )
  491. {
  492. QMetaObject::invokeMethod( this, "replyOptions", Qt::QueuedConnection );
  493. return;
  494. }
  495. alreadyReply_ = true;
  496. if ( ioDevice_.isNull() )
  497. {
  498. qDebug() << "JQHttpServer::Session::replyOptions: error1";
  499. this->deleteLater();
  500. return;
  501. }
  502. const auto &&data2 = replyOptionsFormat.toUtf8();
  503. waitWrittenByteCount_ = data2.size();
  504. ioDevice_->write( data2 );
  505. }
  506. void JQHttpServer::Session::inspectionBufferSetup1()
  507. {
  508. if ( !headerAcceptedFinish_ )
  509. {
  510. forever
  511. {
  512. static QByteArray splitFlag( "\r\n" );
  513. auto splitFlagIndex = buffer_.indexOf( splitFlag );
  514. // 没有获取到分割标记,意味着数据不全
  515. if ( splitFlagIndex == -1 )
  516. {
  517. // 没有获取到 method 但是缓冲区内已经有了数据,这可能是一个无效的连接
  518. if ( requestMethod_.isEmpty() && ( buffer_.size() > 4 ) )
  519. {
  520. qDebug() << "JQHttpServer::Session::inspectionBuffer: error0";
  521. this->deleteLater();
  522. return;
  523. }
  524. return;
  525. }
  526. // 如果未获取到 method 并且已经定位到了分割标记符,那么直接放弃这个连接
  527. if ( requestMethod_.isEmpty() && ( splitFlagIndex == 0 ) )
  528. {
  529. qDebug() << "JQHttpServer::Session::inspectionBuffer: error1";
  530. this->deleteLater();
  531. return;
  532. }
  533. // 如果没有获取到 method 则先尝试分析 method
  534. if ( requestMethod_.isEmpty() )
  535. {
  536. auto requestLineDatas = buffer_.mid( 0, splitFlagIndex ).split( ' ' );
  537. buffer_.remove( 0, splitFlagIndex + 2 );
  538. if ( requestLineDatas.size() != 3 )
  539. {
  540. qDebug() << "JQHttpServer::Session::inspectionBuffer: error2";
  541. this->deleteLater();
  542. return;
  543. }
  544. requestMethod_ = requestLineDatas.at( 0 );
  545. requestUrl_ = requestLineDatas.at( 1 );
  546. requestCrlf_ = requestLineDatas.at( 2 );
  547. if ( ( requestMethod_ != "GET" ) &&
  548. ( requestMethod_ != "OPTIONS" ) &&
  549. ( requestMethod_ != "POST" ) &&
  550. ( requestMethod_ != "PUT" ) )
  551. {
  552. qDebug() << "JQHttpServer::Session::inspectionBuffer: error3:" << requestMethod_;
  553. this->deleteLater();
  554. return;
  555. }
  556. }
  557. else if ( splitFlagIndex == 0 )
  558. {
  559. buffer_.remove( 0, 2 );
  560. // qDebug() << buffer_;
  561. headerAcceptedFinish_ = true;
  562. if ( ( requestMethod_.toUpper() == "GET" ) ||
  563. ( requestMethod_.toUpper() == "OPTIONS" ) ||
  564. ( ( requestMethod_.toUpper() == "POST" ) && ( ( contentLength_ > 0 ) ? ( !buffer_.isEmpty() ) : ( true ) ) ) ||
  565. ( ( requestMethod_.toUpper() == "PUT" ) && ( ( contentLength_ > 0 ) ? ( !buffer_.isEmpty() ) : ( true ) ) ) )
  566. {
  567. this->inspectionBufferSetup2();
  568. }
  569. }
  570. else
  571. {
  572. auto index = buffer_.indexOf( ':' );
  573. if ( index <= 0 )
  574. {
  575. qDebug() << "JQHttpServer::Session::inspectionBuffer: error4";
  576. this->deleteLater();
  577. return;
  578. }
  579. auto headerData = buffer_.mid( 0, splitFlagIndex );
  580. buffer_.remove( 0, splitFlagIndex + 2 );
  581. const auto &&key = headerData.mid( 0, index );
  582. auto value = headerData.mid( index + 1 );
  583. if ( value.startsWith( ' ' ) )
  584. {
  585. value.remove( 0, 1 );
  586. }
  587. requestHeader_[ key ] = value;
  588. if ( key.toLower() == "content-length" )
  589. {
  590. contentLength_ = value.toLongLong();
  591. }
  592. }
  593. }
  594. }
  595. else
  596. {
  597. this->inspectionBufferSetup2();
  598. }
  599. }
  600. void JQHttpServer::Session::inspectionBufferSetup2()
  601. {
  602. requestBody_ += buffer_;
  603. buffer_.clear();
  604. // qDebug() << requestBody_.size() << contentLength_;
  605. if ( !handleAcceptedCallback_ )
  606. {
  607. qDebug() << "JQHttpServer::Session::inspectionBuffer: error4";
  608. this->deleteLater();
  609. return;
  610. }
  611. if ( ( contentLength_ != -1 ) && ( requestBody_.size() != contentLength_ ) )
  612. {
  613. return;
  614. }
  615. handleAcceptedCallback_( this );
  616. }
  617. // AbstractManage
  618. JQHttpServer::AbstractManage::AbstractManage(const int &handleMaxThreadCount)
  619. {
  620. handleThreadPool_.reset( new QThreadPool );
  621. serverThreadPool_.reset( new QThreadPool );
  622. handleThreadPool_->setMaxThreadCount( handleMaxThreadCount );
  623. serverThreadPool_->setMaxThreadCount( 1 );
  624. }
  625. JQHttpServer::AbstractManage::~AbstractManage()
  626. {
  627. this->stopHandleThread();
  628. }
  629. bool JQHttpServer::AbstractManage::begin()
  630. {
  631. if ( QThread::currentThread() != this->thread() )
  632. {
  633. qDebug() << "JQHttpServer::Manage::listen: error: listen from other thread";
  634. return false;
  635. }
  636. if ( this->isRunning() )
  637. {
  638. qDebug() << "JQHttpServer::Manage::close: error: already running";
  639. return false;
  640. }
  641. return this->startServerThread();
  642. }
  643. void JQHttpServer::AbstractManage::close()
  644. {
  645. if ( !this->isRunning() )
  646. {
  647. qDebug() << "JQHttpServer::Manage::close: error: not running";
  648. return;
  649. }
  650. emit readyToClose();
  651. if ( serverThreadPool_->activeThreadCount() )
  652. {
  653. this->stopServerThread();
  654. }
  655. }
  656. bool JQHttpServer::AbstractManage::startServerThread()
  657. {
  658. QSemaphore semaphore;
  659. QtConcurrent::run( serverThreadPool_.data(), [ &semaphore, this ]()
  660. {
  661. QEventLoop eventLoop;
  662. QObject::connect( this, &AbstractManage::readyToClose, &eventLoop, &QEventLoop::quit );
  663. if ( !this->onStart() )
  664. {
  665. semaphore.release( 1 );
  666. }
  667. semaphore.release( 1 );
  668. eventLoop.exec();
  669. this->onFinish();
  670. } );
  671. semaphore.acquire( 1 );
  672. return this->isRunning();
  673. }
  674. void JQHttpServer::AbstractManage::stopHandleThread()
  675. {
  676. handleThreadPool_->waitForDone();
  677. }
  678. void JQHttpServer::AbstractManage::stopServerThread()
  679. {
  680. serverThreadPool_->waitForDone();
  681. }
  682. void JQHttpServer::AbstractManage::newSession(const QPointer< Session > &session)
  683. {
  684. session->setHandleAcceptedCallback( [ this ](const QPointer< JQHttpServer::Session > &session){ this->handleAccepted( session ); } );
  685. auto session_ = session.data();
  686. connect( session.data(), &QObject::destroyed, [ this, session_ ]()
  687. {
  688. this->mutex_.lock();
  689. this->availableSessions_.remove( session_ );
  690. this->mutex_.unlock();
  691. } );
  692. availableSessions_.insert( session.data() );
  693. }
  694. void JQHttpServer::AbstractManage::handleAccepted(const QPointer<Session> &session)
  695. {
  696. QtConcurrent::run( handleThreadPool_.data(), [ this, session ]()
  697. {
  698. if ( !this->httpAcceptedCallback_ )
  699. {
  700. qDebug() << "JQHttpServer::Manage::handleAccepted: error, httpAcceptedCallback_ is nullptr";
  701. return;
  702. }
  703. this->httpAcceptedCallback_( session );
  704. } );
  705. }
  706. // TcpServerManage
  707. JQHttpServer::TcpServerManage::TcpServerManage(const int &handleMaxThreadCount):
  708. AbstractManage( handleMaxThreadCount )
  709. { }
  710. JQHttpServer::TcpServerManage::~TcpServerManage()
  711. {
  712. if ( this->isRunning() )
  713. {
  714. this->close();
  715. }
  716. }
  717. bool JQHttpServer::TcpServerManage::listen(const QHostAddress &address, const quint16 &port)
  718. {
  719. listenAddress_ = address;
  720. listenPort_ = port;
  721. return this->begin();
  722. }
  723. bool JQHttpServer::TcpServerManage::isRunning()
  724. {
  725. return !tcpServer_.isNull();
  726. }
  727. bool JQHttpServer::TcpServerManage::onStart()
  728. {
  729. mutex_.lock();
  730. tcpServer_ = new QTcpServer;
  731. mutex_.unlock();
  732. QObject::connect( tcpServer_.data(), &QTcpServer::newConnection, [ this ]()
  733. {
  734. auto socket = this->tcpServer_->nextPendingConnection();
  735. this->newSession( new Session( socket ) );
  736. } );
  737. if ( !tcpServer_->listen( listenAddress_, listenPort_ ) )
  738. {
  739. mutex_.lock();
  740. delete tcpServer_.data();
  741. tcpServer_.clear();
  742. mutex_.unlock();
  743. return false;
  744. }
  745. return true;
  746. }
  747. void JQHttpServer::TcpServerManage::onFinish()
  748. {
  749. this->mutex_.lock();
  750. tcpServer_->close();
  751. delete tcpServer_.data();
  752. tcpServer_.clear();
  753. this->mutex_.unlock();
  754. }
  755. // SslServerManage
  756. #ifndef QT_NO_SSL
  757. namespace JQHttpServer
  758. {
  759. class SslServerHelper: public QTcpServer
  760. {
  761. void incomingConnection(qintptr socketDescriptor) final
  762. {
  763. onIncomingConnectionCallback_( socketDescriptor );
  764. }
  765. public:
  766. std::function< void(qintptr socketDescriptor) > onIncomingConnectionCallback_;
  767. };
  768. }
  769. JQHttpServer::SslServerManage::SslServerManage(const int &handleMaxThreadCount):
  770. AbstractManage( handleMaxThreadCount )
  771. { }
  772. JQHttpServer::SslServerManage::~SslServerManage()
  773. {
  774. if ( this->isRunning() )
  775. {
  776. this->close();
  777. }
  778. }
  779. bool JQHttpServer::SslServerManage::listen(
  780. const QHostAddress &address,
  781. const quint16 &port,
  782. const QString &crtFilePath,
  783. const QString &keyFilePath,
  784. const QList< QPair< QString, bool > > &caFileList
  785. )
  786. {
  787. listenAddress_ = address;
  788. listenPort_ = port;
  789. QFile fileForCrt( crtFilePath );
  790. if ( !fileForCrt.open( QIODevice::ReadOnly ) )
  791. {
  792. qDebug() << "SslServerManage::listen: error: can not open file:" << crtFilePath;
  793. return false;
  794. }
  795. QFile fileForKey( keyFilePath );
  796. if ( !fileForKey.open( QIODevice::ReadOnly ) )
  797. {
  798. qDebug() << "SslServerManage::listen: error: can not open file:" << keyFilePath;
  799. return false;
  800. }
  801. QSslCertificate sslCertificate( fileForCrt.readAll(), QSsl::Pem );
  802. QSslKey sslKey( fileForKey.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey );
  803. QList< QSslCertificate > caCertificates;
  804. for ( const auto &caFile: caFileList )
  805. {
  806. QFile fileForCa( caFile.first );
  807. if ( !fileForCa.open( QIODevice::ReadOnly ) )
  808. {
  809. qDebug() << "SslServerManage::listen: error: can not open file:" << caFile.first;
  810. return false;
  811. }
  812. caCertificates.push_back( QSslCertificate( fileForCa.readAll(), ( caFile.second ) ? ( QSsl::Pem ) : ( QSsl::Der ) ) );
  813. }
  814. sslConfiguration_.reset( new QSslConfiguration );
  815. sslConfiguration_->setPeerVerifyMode( QSslSocket::VerifyNone );
  816. sslConfiguration_->setLocalCertificate( sslCertificate );
  817. sslConfiguration_->setPrivateKey( sslKey );
  818. sslConfiguration_->setProtocol( QSsl::TlsV1_2 );
  819. sslConfiguration_->setCaCertificates( caCertificates );
  820. qDebug() << "sslCertificate:" << sslCertificate;
  821. qDebug() << "sslKey:" << sslKey;
  822. qDebug() << "caCertificates:" << caCertificates;
  823. return this->begin();
  824. }
  825. bool JQHttpServer::SslServerManage::isRunning()
  826. {
  827. return !tcpServer_.isNull();
  828. }
  829. bool JQHttpServer::SslServerManage::onStart()
  830. {
  831. mutex_.lock();
  832. tcpServer_ = new SslServerHelper;
  833. mutex_.unlock();
  834. tcpServer_->onIncomingConnectionCallback_ = [ this ](qintptr socketDescriptor)
  835. {
  836. auto sslSocket = new QSslSocket;
  837. sslSocket->setSslConfiguration( *sslConfiguration_ );
  838. QObject::connect( sslSocket, &QSslSocket::encrypted, [ this, sslSocket ]()
  839. {
  840. // qDebug() << "SslServerManage::encrypted";
  841. this->newSession( new Session( sslSocket ) );
  842. } );
  843. // QObject::connect( sslSocket, &QSslSocket::modeChanged, [ this, sslSocket ](QSslSocket::SslMode mode)
  844. // {
  845. // qDebug() << "modeChanged" << mode;
  846. // } );
  847. // QObject::connect( sslSocket, (void(QSslSocket::*)(QAbstractSocket::SocketError))&QSslSocket::error, [ sslSocket ](QAbstractSocket::SocketError e)
  848. // {
  849. // qDebug() << e << sslSocket->errorString();
  850. // } );
  851. // QObject::connect( sslSocket, (void(QSslSocket::*)(const QList<QSslError> &))&QSslSocket::sslErrors, [ sslSocket ](const QList<QSslError> &e)
  852. // {
  853. // qDebug() << e;
  854. // } );
  855. sslSocket->setSocketDescriptor( socketDescriptor );
  856. sslSocket->startServerEncryption();
  857. };
  858. if ( !tcpServer_->listen( listenAddress_, listenPort_ ) )
  859. {
  860. mutex_.lock();
  861. delete tcpServer_.data();
  862. tcpServer_.clear();
  863. mutex_.unlock();
  864. return false;
  865. }
  866. return true;
  867. }
  868. void JQHttpServer::SslServerManage::onFinish()
  869. {
  870. this->mutex_.lock();
  871. tcpServer_->close();
  872. delete tcpServer_.data();
  873. tcpServer_.clear();
  874. this->mutex_.unlock();
  875. }
  876. #endif
  877. // LocalServerManage
  878. JQHttpServer::LocalServerManage::LocalServerManage(const int &handleMaxThreadCount):
  879. AbstractManage( handleMaxThreadCount )
  880. { }
  881. JQHttpServer::LocalServerManage::~LocalServerManage()
  882. {
  883. if ( this->isRunning() )
  884. {
  885. this->close();
  886. }
  887. }
  888. bool JQHttpServer::LocalServerManage::listen(const QString &name)
  889. {
  890. listenName_ = name;
  891. return this->begin();
  892. }
  893. bool JQHttpServer::LocalServerManage::isRunning()
  894. {
  895. return !localServer_.isNull();
  896. }
  897. bool JQHttpServer::LocalServerManage::onStart()
  898. {
  899. mutex_.lock();
  900. localServer_ = new QLocalServer;
  901. mutex_.unlock();
  902. QObject::connect( localServer_.data(), &QLocalServer::newConnection, [ this ]()
  903. {
  904. this->newSession( new Session( this->localServer_->nextPendingConnection() ) );
  905. } );
  906. if ( !localServer_->listen( listenName_ ) )
  907. {
  908. mutex_.lock();
  909. delete localServer_.data();
  910. localServer_.clear();
  911. mutex_.unlock();
  912. return false;
  913. }
  914. return true;
  915. }
  916. void JQHttpServer::LocalServerManage::onFinish()
  917. {
  918. this->mutex_.lock();
  919. localServer_->close();
  920. delete localServer_.data();
  921. localServer_.clear();
  922. this->mutex_.unlock();
  923. }