httpprot.cpp 19 KB

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