httpprot.cpp 17 KB

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