httpprot.cpp 19 KB

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