httpprot.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #pragma warning (disable : 4786)
  14. #include "esphttp.hpp"
  15. //Jlib
  16. #include "jliball.hpp"
  17. //SCM Interface definition includes:
  18. #include "esp.hpp"
  19. #include "espthread.hpp"
  20. #include "espplugin.ipp"
  21. //ESP Bindings
  22. #include "http/platform/httpprot.hpp"
  23. #include "http/platform/httptransport.ipp"
  24. #include "http/platform/httpservice.hpp"
  25. #include "SOAP/Platform/soapservice.hpp"
  26. #include "securesocket.hpp"
  27. #define ESP_FACTORY DECL_EXPORT
  28. IThreadPool* http_thread_pool;
  29. CHttpThreadPoolFactory* http_pool_factory;
  30. /**************************************************************************
  31. * CHttpProtocol Implementation *
  32. **************************************************************************/
  33. CHttpProtocol::CHttpProtocol()
  34. {
  35. m_maxConcurrentThreads = 0;
  36. }
  37. CHttpProtocol::~CHttpProtocol()
  38. {
  39. if(http_thread_pool)
  40. {
  41. http_thread_pool->Release();
  42. http_thread_pool = NULL;
  43. }
  44. if(http_pool_factory)
  45. {
  46. http_pool_factory->Release();
  47. http_pool_factory = NULL;
  48. }
  49. }
  50. void CHttpProtocol::init(IPropertyTree * cfg, const char * process, const char * protocol)
  51. {
  52. Owned<IPropertyTree> proc_cfg = getProcessConfig(cfg, process);
  53. if (proc_cfg)
  54. {
  55. CEspProtocol::setViewConfig(proc_cfg->getPropBool("@httpConfigAccess"));
  56. const char* mctstr = proc_cfg->queryProp("@maxConcurrentThreads");
  57. if(mctstr && *mctstr)
  58. {
  59. m_maxConcurrentThreads = atoi(mctstr);
  60. }
  61. m_threadCreateTimeout = 0;
  62. const char* timeoutstr = proc_cfg->queryProp("@threadCreateTimeout");
  63. if(timeoutstr && *timeoutstr)
  64. {
  65. m_threadCreateTimeout = atoi(timeoutstr);
  66. }
  67. if(m_maxConcurrentThreads > 0)
  68. {
  69. // Could use a mutex, but since all the protocols are instantiated sequentially, not really necessary
  70. if(!http_pool_factory)
  71. http_pool_factory = new CHttpThreadPoolFactory();
  72. if(!http_thread_pool)
  73. http_thread_pool = createThreadPool("Http Thread", http_pool_factory, NULL, m_maxConcurrentThreads, INFINITE);
  74. }
  75. }
  76. initPersistentHandler(proc_cfg);
  77. Owned<IPropertyTree> proto_cfg = getProtocolConfig(cfg, protocol, process);
  78. if(proto_cfg)
  79. {
  80. const char* lenstr = proto_cfg->queryProp("@maxRequestEntityLength");
  81. if(lenstr && *lenstr)
  82. {
  83. setMaxRequestEntityLength(atoi(lenstr));
  84. }
  85. }
  86. }
  87. bool CHttpProtocol::notifySelected(ISocket *sock,unsigned selected, IPersistentHandler* persistentHandler, bool shouldClose)
  88. {
  89. try
  90. {
  91. char name[256];
  92. int port = sock->name(name, 255);
  93. CEspApplicationPort *apport = queryApplicationPort(port);
  94. if(apport == NULL)
  95. throw MakeStringException(-1, "binding not found!");
  96. if(apport != NULL)
  97. {
  98. Owned<ISocket> accepted;
  99. if (persistentHandler == nullptr)
  100. accepted.setown(sock->accept());
  101. else
  102. accepted.set(sock);
  103. if (accepted.get() != NULL)
  104. {
  105. char peername[256];
  106. int port = accepted->peer_name(peername, 256);
  107. #if defined(_DEBUG)
  108. DBGLOG("HTTP connection from %s:%d on %s socket", peername, port, persistentHandler?"persistent":"new");
  109. #endif
  110. if(m_maxConcurrentThreads > 0)
  111. {
  112. // Using Threading pool instead of generating one thread per request.
  113. void ** holder = new void*[7];
  114. holder[0] = (void*)(accepted.getLink());
  115. holder[1] = (void*)apport;
  116. int maxEntityLength = getMaxRequestEntityLength();
  117. holder[2] = (void*)&maxEntityLength;
  118. bool useSSL = false;
  119. holder[3] = (void*)&useSSL;
  120. ISecureSocketContext* ctx = NULL;
  121. holder[4] = (void*)ctx;
  122. holder[5] = (void*)persistentHandler;
  123. holder[6] = (void*)&shouldClose;
  124. try
  125. {
  126. http_thread_pool->start((void*)holder, "", m_threadCreateTimeout > 0?m_threadCreateTimeout*1000:0);
  127. }
  128. catch(...)
  129. {
  130. IERRLOG("Error starting thread from http thread pool.");
  131. if(accepted.get())
  132. {
  133. accepted->close();
  134. //Assumption here is that if start() throws exception, that means the new
  135. //thread hasn't been started, so there's no other thread holding a link.
  136. CInterface* ci = dynamic_cast<CInterface*>(accepted.get());
  137. if(ci && ci->IsShared())
  138. accepted->Release();
  139. }
  140. delete [] holder;
  141. throw;
  142. }
  143. delete [] holder;
  144. }
  145. else
  146. {
  147. /* create one thread per request */
  148. CHttpThread *workthread = new CHttpThread(accepted.getLink(), apport, CEspProtocol::getViewConfig(), false, nullptr, persistentHandler);
  149. workthread->setMaxRequestEntityLength(getMaxRequestEntityLength());
  150. workthread->setShouldClose(shouldClose);
  151. workthread->start();
  152. workthread->Release();
  153. }
  154. }
  155. }
  156. else
  157. {
  158. throw MakeStringException(-1, "can't acquire bindings IEspHttpBinding interface (via dynamic_cast)!");
  159. }
  160. }
  161. catch (IException *e)
  162. {
  163. StringBuffer estr;
  164. IERRLOG("Exception(%d, %s) in CHttpProtocol::notifySelected()", e->errorCode(), e->errorMessage(estr).str());
  165. e->Release();
  166. }
  167. catch(...)
  168. {
  169. IERRLOG("Unknown Exception in CHttpProtocol::notifySelected()");
  170. }
  171. return false;
  172. }
  173. //IEspProtocol
  174. const char * CHttpProtocol::getProtocolName()
  175. {
  176. return "http_protocol";
  177. }
  178. /**************************************************************************
  179. * CSecureHttpProtocol Implementation *
  180. **************************************************************************/
  181. CSecureHttpProtocol::CSecureHttpProtocol(IPropertyTree* cfg)
  182. {
  183. m_maxConcurrentThreads = 0;
  184. if(cfg != NULL)
  185. {
  186. m_config.setown(cfg);
  187. //ensure keys are specified. Passphrase is optional
  188. StringBuffer sb;
  189. cfg->getProp("certificate", sb);
  190. if(sb.length() == 0)
  191. {
  192. throw MakeStringException(-1, "certificate file not specified in config file");
  193. }
  194. cfg->getProp("privatekey", sb.clear());
  195. if(sb.length() == 0)
  196. {
  197. throw MakeStringException(-1, "private key file not specified in config file");
  198. }
  199. createSecureSocketContextEx2_t xproc = NULL;
  200. IEspPlugin *pplg = loadPlugin(SSLIB);
  201. if (pplg)
  202. xproc = (createSecureSocketContextEx2_t) pplg->getProcAddress("createSecureSocketContextEx2");
  203. else
  204. throw MakeStringException(-1, "dll/shared-object %s can't be loaded", SSLIB);
  205. if (xproc)
  206. m_ssctx.setown(xproc(cfg, ServerSocket));
  207. else
  208. throw MakeStringException(-1, "procedure createSecureSocketContextEx2 can't be loaded");
  209. }
  210. }
  211. CSecureHttpProtocol::~CSecureHttpProtocol()
  212. {
  213. if(http_thread_pool)
  214. {
  215. http_thread_pool->Release();
  216. http_thread_pool = NULL;
  217. }
  218. if(http_pool_factory)
  219. {
  220. http_pool_factory->Release();
  221. http_pool_factory = NULL;
  222. }
  223. }
  224. void CSecureHttpProtocol::init(IPropertyTree * cfg, const char * process, const char * protocol)
  225. {
  226. Owned<IPropertyTree> proc_cfg = getProcessConfig(cfg, process);
  227. if (proc_cfg)
  228. {
  229. CEspProtocol::setViewConfig(proc_cfg->getPropBool("@httpConfigAccess"));
  230. const char* mctstr = proc_cfg->queryProp("@maxConcurrentThreads");
  231. if(mctstr && *mctstr)
  232. {
  233. m_maxConcurrentThreads = atoi(mctstr);
  234. }
  235. if(m_maxConcurrentThreads > 0)
  236. {
  237. // Could use a mutex, but since all the protocols are instantiated sequentially, not really necessary
  238. if(!http_pool_factory)
  239. http_pool_factory = new CHttpThreadPoolFactory();
  240. if(!http_thread_pool)
  241. http_thread_pool = createThreadPool("Http Thread", http_pool_factory, NULL, m_maxConcurrentThreads, INFINITE);
  242. }
  243. }
  244. initPersistentHandler(proc_cfg);
  245. Owned<IPropertyTree> proto_cfg = getProtocolConfig(cfg, protocol, process);
  246. if(proto_cfg)
  247. {
  248. const char* lenstr = proto_cfg->queryProp("@maxRequestEntityLength");
  249. if(lenstr && *lenstr)
  250. {
  251. setMaxRequestEntityLength(atoi(lenstr));
  252. }
  253. }
  254. }
  255. bool CSecureHttpProtocol::notifySelected(ISocket *sock,unsigned selected, IPersistentHandler* persistentHandler, bool shouldClose)
  256. {
  257. try
  258. {
  259. char name[256];
  260. int port = sock->name(name, 255);
  261. CEspApplicationPort *apport = queryApplicationPort(port);
  262. if(apport == NULL)
  263. throw MakeStringException(-1, "binding not found!");
  264. if(apport != NULL)
  265. {
  266. Owned<ISocket>accepted;
  267. if(persistentHandler == nullptr)
  268. accepted.setown(sock->accept());
  269. else
  270. accepted.set(sock);
  271. if (accepted.get() != NULL)
  272. {
  273. char peername[256];
  274. int port = accepted->peer_name(peername, 256);
  275. DBGLOG("HTTPS connection from %s:%d on %s socket", peername, port, persistentHandler?"persistent":"new");
  276. if(m_ssctx != NULL)
  277. {
  278. if(m_maxConcurrentThreads > 0)
  279. {
  280. // Using Threading pool instead of generating one thread per request.
  281. void ** holder = new void*[7];
  282. holder[0] = (void*)accepted.getLink();
  283. holder[1] = (void*)apport;
  284. int maxEntityLength = getMaxRequestEntityLength();
  285. holder[2] = (void*)&maxEntityLength;
  286. bool useSSL = true;
  287. holder[3] = (void*)&useSSL;
  288. holder[4] = (void*)m_ssctx.get();
  289. holder[5] = (void*)persistentHandler;
  290. holder[6] = (void*)&shouldClose;
  291. http_thread_pool->start((void*)holder);
  292. delete [] holder;
  293. }
  294. else
  295. {
  296. /* create one thread per request */
  297. CHttpThread *workthread = new CHttpThread(accepted.getLink(), apport, CEspProtocol::getViewConfig(), true, m_ssctx.get(), persistentHandler);
  298. workthread->setMaxRequestEntityLength(getMaxRequestEntityLength());
  299. workthread->setShouldClose(shouldClose);
  300. workthread->start();
  301. ESPLOG(LogMax, "Request processing thread started.");
  302. workthread->Release();
  303. }
  304. }
  305. else
  306. {
  307. return false;
  308. }
  309. }
  310. }
  311. else
  312. {
  313. throw MakeStringException(-1, "can't acquire bindings IEspHttpBinding interface (via dynamic_cast)!");
  314. }
  315. }
  316. catch (IException *e)
  317. {
  318. StringBuffer estr;
  319. IERRLOG("Exception(%d, %s) in CSecureHttpProtocol::notifySelected()", e->errorCode(), e->errorMessage(estr).str());
  320. e->Release();
  321. }
  322. catch(...)
  323. {
  324. IERRLOG("Unknown Exception in CSecureHttpProtocol::notifySelected()");
  325. }
  326. return false;
  327. }
  328. //IEspProtocol
  329. const char * CSecureHttpProtocol::getProtocolName()
  330. {
  331. return "secure_http_protocol";
  332. }
  333. /**************************************************************************
  334. * CHttpThread Implementation *
  335. **************************************************************************/
  336. CHttpThread::CHttpThread(bool viewConfig) :
  337. CEspProtocolThread("Http Thread")
  338. {
  339. m_viewConfig = viewConfig;
  340. m_is_ssl = false;
  341. m_ssctx = NULL;
  342. }
  343. CHttpThread::CHttpThread(ISocket *sock, bool viewConfig) :
  344. CEspProtocolThread(sock, "HTTP Thread")
  345. {
  346. m_viewConfig = viewConfig;
  347. m_is_ssl = false;
  348. m_ssctx = NULL;
  349. }
  350. CHttpThread::CHttpThread(ISocket *sock, CEspApplicationPort* apport, bool viewConfig, bool isSSL, ISecureSocketContext* ssctx, IPersistentHandler* persistentHandler) :
  351. CEspProtocolThread(sock, "HTTP Thread"), m_persistentHandler(persistentHandler)
  352. {
  353. m_viewConfig = viewConfig;
  354. m_apport = apport;
  355. m_is_ssl = isSSL;
  356. m_ssctx = ssctx;
  357. }
  358. CHttpThread::~CHttpThread()
  359. {
  360. }
  361. bool CHttpThread::onRequest()
  362. {
  363. keepAlive = false;
  364. ActiveRequests recording;
  365. Owned<CEspHttpServer> httpserver;
  366. Owned<ISecureSocket> secure_sock;
  367. if(m_is_ssl && m_ssctx && m_persistentHandler == nullptr)
  368. {
  369. ESPLOG(LogMax, "Creating secure socket");
  370. secure_sock.setown(m_ssctx->createSecureSocket(m_socket.getLink(), getEspLogLevel()));
  371. int res = 0;
  372. try
  373. {
  374. ESPLOG(LogMax, "Accepting from secure socket");
  375. res = secure_sock->secure_accept();
  376. if(res < 0)
  377. {
  378. ESPLOG(LogMin, "Error accepting from secure socket");
  379. return false;
  380. }
  381. }
  382. catch(IException* e)
  383. {
  384. StringBuffer emsg;
  385. e->errorMessage(emsg);
  386. IERRLOG("%s", emsg.str());
  387. return false;
  388. }
  389. catch(...)
  390. {
  391. IERRLOG("Unknown exception accepting from secure socket");
  392. return false;
  393. }
  394. ESPLOG(LogMax, "Request from secure socket");
  395. m_socket.set(secure_sock);
  396. httpserver.setown(new CEspHttpServer(*secure_sock.get(), m_apport, m_viewConfig, getMaxRequestEntityLength()));
  397. }
  398. else
  399. {
  400. httpserver.setown(new CEspHttpServer(*m_socket, m_apport, m_viewConfig, getMaxRequestEntityLength()));
  401. }
  402. time_t t = time(NULL);
  403. initThreadLocal(sizeof(t), &t);
  404. m_httpserver = httpserver;
  405. httpserver->setSocketReturner(this);
  406. httpserver->setIsSSL(m_is_ssl);
  407. httpserver->setShouldClose(m_shouldClose);
  408. httpserver->processRequest();
  409. returnSocket(false);
  410. clearThreadLocal();
  411. return false;
  412. }
  413. void CHttpThread::returnSocket(bool cascade)
  414. {
  415. if (m_httpSocketReturned)
  416. return;
  417. m_httpSocketReturned = true;
  418. CEspHttpServer* httpserver = dynamic_cast<CEspHttpServer*>(m_httpserver);
  419. if (m_persistentHandler == nullptr)
  420. {
  421. keepAlive = !m_shouldClose && m_apport->queryProtocol()->persistentEnabled() && httpserver->persistentEligible();
  422. if (keepAlive)
  423. m_apport->queryProtocol()->addPersistent(m_socket.get());
  424. }
  425. else
  426. {
  427. keepAlive = !m_shouldClose && httpserver->persistentEligible();
  428. m_persistentHandler->doneUsing(m_socket, keepAlive);
  429. }
  430. if (cascade)
  431. CEspProtocolThread::returnSocket();
  432. }
  433. void CHttpThread::returnSocket()
  434. {
  435. returnSocket(true);
  436. }
  437. /**************************************************************************
  438. * CPooledHttpThread Implementation *
  439. **************************************************************************/
  440. void CPooledHttpThread::init(void *param)
  441. {
  442. m_socket.setown((ISocket*)(((void**)param)[0]));
  443. m_apport = (CEspApplicationPort*)(((void**)param)[1]);
  444. m_MaxRequestEntityLength = *(int*)(((void**)param)[2]);
  445. m_is_ssl = *(bool*)(((void**)param)[3]);
  446. m_ssctx = (ISecureSocketContext*)(((void**)param)[4]);
  447. m_persistentHandler = (IPersistentHandler*)(((void**)param)[5]);
  448. m_shouldClose = *(bool*)(((void**)param)[6]);
  449. m_httpserver = nullptr;
  450. m_processAborted = false;
  451. m_socketReturned = false;
  452. }
  453. CPooledHttpThread::~CPooledHttpThread()
  454. {
  455. }
  456. void CPooledHttpThread::threadmain()
  457. {
  458. TimeSection timing("CPooledHttpThread::threadmain()");
  459. Owned<CEspHttpServer> httpserver;
  460. Owned<ISecureSocket> secure_sock;
  461. if(m_is_ssl && m_ssctx && m_persistentHandler == nullptr)
  462. {
  463. secure_sock.setown(m_ssctx->createSecureSocket(m_socket.getLink(), getEspLogLevel()));
  464. int res = 0;
  465. try
  466. {
  467. res = secure_sock->secure_accept();
  468. if(res < 0)
  469. {
  470. return;
  471. }
  472. }
  473. catch(IException* e)
  474. {
  475. StringBuffer emsg;
  476. e->errorMessage(emsg);
  477. IERRLOG("%s", emsg.str());
  478. return;
  479. }
  480. catch(...)
  481. {
  482. return;
  483. }
  484. m_socket.set(secure_sock);
  485. httpserver.setown(new CEspHttpServer(*m_socket, m_apport, false, getMaxRequestEntityLength()));
  486. }
  487. else
  488. {
  489. httpserver.setown(new CEspHttpServer(*m_socket, m_apport, false, getMaxRequestEntityLength()));
  490. }
  491. m_httpserver = httpserver;
  492. httpserver->setShouldClose(m_shouldClose);
  493. httpserver->setSocketReturner(this);
  494. time_t t = time(NULL);
  495. initThreadLocal(sizeof(t), &t);
  496. try
  497. {
  498. ESP_TIME_SECTION("CPooledHttpThread::threadmain: httpserver->processRequest()");
  499. httpserver->processRequest();
  500. }
  501. catch (IException *e)
  502. {
  503. m_processAborted = true;
  504. StringBuffer estr;
  505. IERRLOG("Exception(%d, %s) in CPooledHttpThread::threadmain().", e->errorCode(), e->errorMessage(estr).str());
  506. e->Release();
  507. }
  508. catch(...)
  509. {
  510. m_processAborted = true;
  511. IERRLOG("General Exception - in CPooledHttpThread::threadmain().");
  512. }
  513. returnSocket();
  514. clearThreadLocal();
  515. }
  516. void CPooledHttpThread::returnSocket()
  517. {
  518. if (m_socketReturned)
  519. return;
  520. m_socketReturned = true;
  521. CEspHttpServer* httpserver = dynamic_cast<CEspHttpServer*>(m_httpserver);
  522. bool keepAlive = false;
  523. if (!m_processAborted)
  524. {
  525. if (m_persistentHandler == nullptr)
  526. {
  527. keepAlive = !m_shouldClose && m_apport->queryProtocol()->persistentEnabled() && httpserver->persistentEligible();
  528. if (keepAlive)
  529. m_apport->queryProtocol()->addPersistent(m_socket.get());
  530. }
  531. else
  532. {
  533. keepAlive = !m_shouldClose && httpserver->persistentEligible();
  534. m_persistentHandler->doneUsing(m_socket, keepAlive);
  535. }
  536. }
  537. try
  538. {
  539. if (m_socket != nullptr)
  540. {
  541. if (!keepAlive)
  542. m_socket->shutdown(SHUTDOWN_WRITE);
  543. m_socket.clear();
  544. }
  545. }
  546. catch (IException *e)
  547. {
  548. StringBuffer estr;
  549. IERRLOG("Exception(%d, %s) - CPooledHttpThread::threadmain(), closing socket.", e->errorCode(), e->errorMessage(estr).str());
  550. e->Release();
  551. }
  552. catch(...)
  553. {
  554. IERRLOG("General Exception - CPooledHttpThread::threadmain(), closing socket.");
  555. }
  556. }