httpclient.cpp 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076
  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. //Jlib
  15. #include "jliball.hpp"
  16. //ESP Bindings
  17. #include "httpclient.ipp"
  18. #include "http/platform/httptransport.hpp"
  19. #include "securesocket.hpp"
  20. #include "bindutil.hpp"
  21. #include "espplugin.ipp"
  22. #include "SOAP/Platform/soapmessage.hpp"
  23. static Owned<CHttpClientContext> theHttpClientContext;
  24. static MapStringToMyClass<CHttpClientContext> httpClientContextsUsingSecrets;
  25. static CriticalSection httpCrit;
  26. MODULE_INIT(INIT_PRIORITY_STANDARD)
  27. {
  28. return true;
  29. }
  30. MODULE_EXIT()
  31. {
  32. theHttpClientContext.clear();
  33. httpClientContextsUsingSecrets.kill();
  34. }
  35. /*************************************************************************
  36. CHttpClient Implementation
  37. **************************************************************************/
  38. #define URL_MAX 512
  39. CHttpClientContext::CHttpClientContext()
  40. {
  41. initPersistentHandler();
  42. }
  43. CHttpClientContext::CHttpClientContext(IPropertyTree* config) : m_config(config)
  44. {
  45. initPersistentHandler();
  46. }
  47. void CHttpClientContext::initPersistentHandler()
  48. {
  49. IEspServer* server = queryEspServer();
  50. IPropertyTree* proc_cfg = nullptr;
  51. if (server)
  52. proc_cfg = server->queryProcConfig();
  53. int maxIdleTime = DEFAULT_MAX_PERSISTENT_IDLE_TIME;
  54. int maxReqs = DEFAULT_MAX_PERSISTENT_REQUESTS;
  55. if (proc_cfg)
  56. {
  57. maxIdleTime = proc_cfg->getPropInt("@maxPersistentIdleTime", maxIdleTime);
  58. maxReqs = proc_cfg->getPropInt("@maxPersistentRequests", maxReqs);
  59. }
  60. //To disable persistent connections, set maxPersistentIdleTime or maxPersistentRequests to 0
  61. if (maxIdleTime == 0 || maxReqs == 0)
  62. {
  63. DBGLOG("Persistent connection won't be enabled for httpclient because maxPersistentIdleTime or maxPersistentRequests is set to 0");
  64. return;
  65. }
  66. m_persistentHandler.setown(createPersistentHandler(nullptr, maxIdleTime, maxReqs, static_cast<PersistentLogLevel>(getEspLogLevel()), true));
  67. }
  68. CHttpClientContext::~CHttpClientContext()
  69. {
  70. if (m_persistentHandler.get())
  71. m_persistentHandler->stop(true);
  72. }
  73. IHttpClient* CHttpClientContext::createHttpClient(const char* proxy, const char* url)
  74. {
  75. CHttpClient* client = new CHttpClient(proxy, url);
  76. if(url != NULL && Utils::strncasecmp(url, "HTTPS://", 8) == 0)
  77. {
  78. CriticalBlock b(m_sscrit);
  79. if(m_ssctx.get() == NULL)
  80. {
  81. StringBuffer libName;
  82. IEspPlugin *pplg = loadPlugin(libName.append(SharedObjectPrefix).append(SSLIB).append(SharedObjectExtension));
  83. if (!pplg)
  84. throw MakeStringException(-1, "dll/shared-object %s can't be loaded", libName.str());
  85. if(m_config.get() == NULL)
  86. {
  87. createSecureSocketContextSecret_t xproc = NULL;
  88. xproc = (createSecureSocketContextSecret_t) pplg->getProcAddress("createSecureSocketContextSecret");
  89. if (xproc)
  90. m_ssctx.setown(xproc(m_mtls_secret.str(), ClientSocket));
  91. else
  92. throw MakeStringException(-1, "procedure createSecureSocketContextSecret can't be loaded");
  93. }
  94. else
  95. {
  96. createSecureSocketContextEx2_t xproc = NULL;
  97. xproc = (createSecureSocketContextEx2_t) pplg->getProcAddress("createSecureSocketContextEx2");
  98. if (xproc)
  99. m_ssctx.setown(xproc(m_config.get(),ClientSocket));
  100. else
  101. throw MakeStringException(-1, "procedure createSecureSocketContext can't be loaded");
  102. }
  103. if(m_ssctx.get() == NULL)
  104. throw MakeStringException(-1, "SecureSocketContext can't be created");
  105. }
  106. client->setSsCtx(m_ssctx.get());
  107. }
  108. if (m_persistentHandler)
  109. client->setPersistentHandler(m_persistentHandler);
  110. #ifdef COOKIE_HANDLING
  111. client->m_context = this;
  112. if(url && *url)
  113. {
  114. ReadLockBlock rblock(m_rwlock);
  115. StringBuffer host, protocol, user, passwd, port, path;
  116. Utils::SplitURL(url, protocol, user, passwd, host, port, path);
  117. if(host.length() > 0)
  118. {
  119. ForEachItemIn(x, m_cookies)
  120. {
  121. CEspCookie* cookie = &m_cookies.item(x);
  122. if(!cookie)
  123. continue;
  124. const char* chost = cookie->getHost();
  125. if(chost && stricmp(chost, host.str()) == 0)
  126. {
  127. //TODO: is it better to clone the cookie?
  128. client->m_request_cookies.append(*LINK(cookie));
  129. }
  130. }
  131. }
  132. }
  133. #endif
  134. return client;
  135. }
  136. CHttpClient::CHttpClient(const char *proxy, const char* url) : m_proxy(proxy), m_url(url), m_disableKeepAlive(false), m_isPersistentSocket(false), m_numRequests(0), m_persistable(false)
  137. {
  138. StringBuffer protocol,username,password, host, port, path;
  139. Utils::SplitURL(url, protocol,username,password, host, port, path);
  140. m_protocol.set(protocol.str());
  141. m_host.set(host.str());
  142. if(port.length() > 0)
  143. m_port = atoi(port.str());
  144. else
  145. if(Utils::strncasecmp(url, "HTTPS://", 8) == 0)
  146. m_port = 443;
  147. else
  148. m_port = 80;
  149. m_path.set(path.str());
  150. m_socket = NULL;
  151. }
  152. CHttpClient::~CHttpClient()
  153. {
  154. if (m_socket)
  155. {
  156. Owned<ISocket> forRelease(m_socket);
  157. if (m_persistentHandler && m_isPersistentSocket)
  158. {
  159. m_persistentHandler->doneUsing(m_socket, !m_disableKeepAlive && m_persistable, m_numRequests>1?(m_numRequests-1):0);
  160. }
  161. else if (m_persistentHandler && !m_disableKeepAlive && m_persistable)
  162. {
  163. m_persistentHandler->add(m_socket, &m_ep, strieq(m_protocol.get(), "HTTPS")?PersistentProtocol::ProtoTLS:PersistentProtocol::ProtoTCP);
  164. }
  165. else
  166. {
  167. try
  168. {
  169. m_socket->shutdown();
  170. m_socket->close();
  171. }
  172. catch(...)
  173. {
  174. }
  175. }
  176. }
  177. }
  178. void CHttpClient::setSsCtx(ISecureSocketContext* ctx)
  179. {
  180. m_ssctx = ctx;
  181. }
  182. void CHttpClient::setUserID(const char* userid)
  183. {
  184. m_userid.set(userid);
  185. }
  186. void CHttpClient::setPassword(const char* password)
  187. {
  188. m_password.set(password);
  189. }
  190. void CHttpClient::setRealm(const char* realm)
  191. {
  192. m_realm.set(realm);
  193. }
  194. void CHttpClient::setProxy(const char* proxy)
  195. {
  196. m_proxy.clear().append(proxy);
  197. }
  198. void CHttpClient::setConnectTimeOutMs(unsigned timeout)
  199. {
  200. m_connectTimeoutMs = timeout;
  201. }
  202. void CHttpClient::setTimeOut(unsigned int timeout)
  203. {
  204. m_readTimeoutSecs = timeout;
  205. }
  206. IMultiException* CHttpClient::queryExceptions()
  207. {
  208. return m_exceptions.get();
  209. }
  210. int CHttpClient::connect(StringBuffer& errmsg, bool forceNewConnection)
  211. {
  212. if (m_socket != nullptr)
  213. {
  214. if (m_persistentHandler && m_isPersistentSocket)
  215. m_persistentHandler->doneUsing(m_socket, false, 0);
  216. close();
  217. }
  218. SocketEndpoint ep;
  219. if(m_proxy.length() == 0)
  220. {
  221. if(m_host.length() <= 0)
  222. throw MakeStringException(-1, "host not specified");
  223. if (!ep.set(m_host.get(), m_port))
  224. {
  225. errmsg.appendf("Bad host name/ip: %s", m_host.get());
  226. UERRLOG("%s", errmsg.str());
  227. return -1;
  228. }
  229. //TODO: should it be 443 for HTTPS??
  230. if (ep.port==0)
  231. ep.port=80;
  232. }
  233. else
  234. {
  235. if (!ep.set(m_proxy.str()))
  236. {
  237. errmsg.appendf("Bad proxy name/ip: %s", m_proxy.str());
  238. UERRLOG("%s", errmsg.str());
  239. return -1;
  240. }
  241. //TODO: should it be 443 for HTTPS??
  242. if (ep.port==0)
  243. ep.port=80;
  244. }
  245. m_ep = ep;
  246. if (!m_persistentHandler || m_persistentHandler->inDoNotReuseList(&ep))
  247. m_disableKeepAlive = true;
  248. bool shouldClose = false;
  249. Owned<ISocket> pSock = (m_disableKeepAlive || forceNewConnection)?nullptr:m_persistentHandler->getAvailable(&ep, &shouldClose,
  250. strieq(m_protocol.get(), "HTTPS")?PersistentProtocol::ProtoTLS:PersistentProtocol::ProtoTCP);
  251. if(pSock)
  252. {
  253. m_isPersistentSocket = true;
  254. if (shouldClose)
  255. m_disableKeepAlive = true;
  256. m_socket = pSock.getLink();
  257. }
  258. else
  259. {
  260. m_isPersistentSocket = false;
  261. try
  262. {
  263. m_socket = ISocket::connect_timeout(ep, m_connectTimeoutMs);
  264. if(strcmp(m_protocol.get(), "HTTPS") == 0)
  265. {
  266. ISecureSocket* securesocket = m_ssctx->createSecureSocket(m_socket);
  267. int res = securesocket->secure_connect();
  268. if(res < 0)
  269. {
  270. close();
  271. }
  272. else
  273. {
  274. m_socket = securesocket;
  275. }
  276. }
  277. }
  278. catch(IException *e)
  279. {
  280. StringBuffer url;
  281. UERRLOG("Error connecting to %s", ep.getUrlStr(url).str());
  282. DBGLOG(e);
  283. e->Release();
  284. m_socket = nullptr;
  285. return -1;
  286. }
  287. catch(...)
  288. {
  289. StringBuffer url;
  290. UERRLOG("Unknown exception connecting to %s", ep.getUrlStr(url).str());
  291. m_socket = nullptr;
  292. return -1;
  293. }
  294. }
  295. if(m_socket == nullptr)
  296. {
  297. StringBuffer urlstr;
  298. DBGLOG(">>Can't connect to %s", ep.getUrlStr(urlstr).str());
  299. return -1;
  300. }
  301. return 0;
  302. }
  303. void CHttpClient::close()
  304. {
  305. if (m_socket == nullptr)
  306. return;
  307. m_socket->shutdown();
  308. m_socket->close();
  309. m_socket->Release();
  310. m_socket = nullptr;
  311. }
  312. int CHttpClient::sendRequest(const char* method, const char* contenttype, StringBuffer& request, StringBuffer& response)
  313. {
  314. HttpClientErrCode ret = sendRequest(method, contenttype, request, response, false);
  315. if (ret == HttpClientErrCode::PeerClosed)
  316. ret = sendRequest(method, contenttype, request, response, true);
  317. return static_cast<int>(ret);
  318. }
  319. HttpClientErrCode CHttpClient::sendRequest(const char* method, const char* contenttype, StringBuffer& request, StringBuffer& response, bool forceNewConnection)
  320. {
  321. StringBuffer errmsg;
  322. if (connect(errmsg, forceNewConnection) < 0)
  323. {
  324. response.append(errmsg);
  325. return HttpClientErrCode::Error;
  326. }
  327. Owned<CHttpRequest> httprequest;
  328. Owned<CHttpResponse> httpresponse;
  329. httprequest.setown(new CHttpRequest(*m_socket));
  330. httpresponse.setown(new CHttpResponse(*m_socket));
  331. httprequest->enableCompression();
  332. httpresponse->enableCompression();
  333. httprequest->setMethod(method);
  334. httprequest->setVersion("HTTP/1.1");
  335. if(m_proxy.length() <= 0)
  336. {
  337. httprequest->setPath(m_path.get());
  338. }
  339. else
  340. {
  341. httprequest->setPath(m_url.get());
  342. }
  343. httprequest->setHost(m_host.get());
  344. httprequest->setPort(m_port);
  345. httprequest->setContentType(contenttype);
  346. if(m_userid.length() > 0)
  347. {
  348. StringBuffer uidpair;
  349. uidpair.append(m_userid.get()).append(":").append(m_password.get());
  350. StringBuffer authhdr("Basic ");
  351. JBASE64_Encode(uidpair.str(), uidpair.length(), authhdr, false);
  352. httprequest->addHeader("Authorization", authhdr.str());
  353. }
  354. if(m_realm.length() > 0)
  355. {
  356. StringBuffer authheader;
  357. authheader.append("Basic realm=\"").append(m_realm).append("\"");
  358. httprequest->addHeader("WWW-Authenticate", authheader.str());
  359. }
  360. if (getEspLogLevel()>LogNormal)
  361. {
  362. DBGLOG("Content type: %s", contenttype);
  363. DBGLOG("Request content: %s", request.str());
  364. }
  365. httprequest->setContent(request.str());
  366. #ifdef COOKIE_HANDLING
  367. ForEachItemIn(x, m_request_cookies)
  368. {
  369. CEspCookie* cookie = &m_request_cookies.item(x);
  370. if(cookie)
  371. httprequest->addCookie(LINK(cookie));
  372. }
  373. #endif
  374. httprequest->setPersistentEnabled(!m_disableKeepAlive);
  375. httprequest->send();
  376. if (m_readTimeoutSecs)
  377. httpresponse->setTimeOut(m_readTimeoutSecs);
  378. m_exceptions.setown(MakeMultiException());
  379. int ret = httpresponse->receive(false, m_exceptions);
  380. if (ret < 0 && m_isPersistentSocket && httpresponse->getPeerClosed())
  381. return HttpClientErrCode::PeerClosed;
  382. #ifdef COOKIE_HANDLING
  383. if(m_context)
  384. {
  385. IArrayOf<CEspCookie>& cookies = httpresponse->queryCookies();
  386. ForEachItemIn(x, cookies)
  387. {
  388. CEspCookie* cookie = &cookies.item(x);
  389. if(!cookie)
  390. continue;
  391. cookie->setHost(m_host.get());
  392. m_context->addCookie(cookie);
  393. }
  394. }
  395. #endif
  396. httpresponse->getContent(response);
  397. m_persistable = httpresponse->getPersistentEligible();
  398. m_numRequests++;
  399. if (getEspLogLevel()>LogNormal)
  400. DBGLOG("Response content: %s", response.str());
  401. return HttpClientErrCode::OK;
  402. }
  403. bool appendProxyPeerAddress(CHttpMessage &copyTo, ISocket *sock, const char *src)
  404. {
  405. if (!sock)
  406. return false;
  407. IpAddress ip;
  408. sock->getPeerAddress(ip);
  409. StringBuffer s(src);
  410. if (s.length())
  411. s.append(", ");
  412. ip.getIpText(s);
  413. copyTo.setHeader("X-Forwarded-For", s);
  414. return true;
  415. }
  416. void copyHeaders(CHttpMessage &copyTo, CHttpMessage &copyFrom, bool resetForwardedFor, bool updateForwardedFor)
  417. {
  418. bool copiedForwardedFor = false;
  419. if (copyFrom.queryHeaders().ordinality())
  420. {
  421. ForEachItemIn(i, copyFrom.queryHeaders())
  422. {
  423. StringArray pair;
  424. pair.appendList(copyFrom.queryHeaders().item(i), ":", true);
  425. const char *name = pair.item(0);
  426. switch (*name)
  427. {
  428. case 'H':
  429. case 'h':
  430. if (strieq(name, "Host"))
  431. continue;
  432. break;
  433. case 'C':
  434. case 'c':
  435. if (strnicmp(name, "Content-", 8)==0)
  436. {
  437. if (strieq(name+8, "Length") || strieq(name+8, "Type"))
  438. continue;
  439. }
  440. break;
  441. case 'X':
  442. if (strieq(name, "X-Forwarded-For"))
  443. {
  444. if (resetForwardedFor)
  445. continue;
  446. if (!updateForwardedFor)
  447. break;
  448. copiedForwardedFor = appendProxyPeerAddress(copyTo, copyFrom.getSocket(), pair.item(1));
  449. continue;
  450. }
  451. break;
  452. default:
  453. break;
  454. }
  455. copyTo.setHeader(name, pair.item(1));
  456. }
  457. if (updateForwardedFor && !copiedForwardedFor)
  458. appendProxyPeerAddress(copyTo, copyFrom.getSocket(), nullptr);
  459. }
  460. }
  461. void copyCookies(CHttpMessage &copyTo, CHttpMessage &copyFrom, const char *host)
  462. {
  463. IArrayOf<CEspCookie>& cookies = copyFrom.queryCookies();
  464. ForEachItemIn(x, cookies)
  465. {
  466. CEspCookie* cookie = &cookies.item(x);
  467. if(!cookie)
  468. continue;
  469. cookie->setHost(host); //unfortunately changes copyFrom cookie... should make a true copy of cookie
  470. copyTo.addCookie(cookie);
  471. }
  472. }
  473. int CHttpClient::proxyRequest(IHttpMessage *request, IHttpMessage *response, bool resetForwardedFor)
  474. {
  475. HttpClientErrCode ret = proxyRequest(request, response, false, resetForwardedFor);
  476. if (ret == HttpClientErrCode::PeerClosed)
  477. ret = proxyRequest(request, response, true, resetForwardedFor);
  478. return static_cast<int>(ret);
  479. }
  480. HttpClientErrCode CHttpClient::proxyRequest(IHttpMessage *request, IHttpMessage *response, bool forceNewConnection, bool resetForwardedFor)
  481. {
  482. CHttpRequest *forwardRequest = static_cast<CHttpRequest*>(request);
  483. assertex(forwardRequest != nullptr);
  484. StringBuffer forwardFor;
  485. if (forwardRequest->getHeader("HPCC-Forward-For", forwardFor).length())
  486. throw MakeStringExceptionDirect(-1, "Only one HPCC-Forward-For hop currently allowed");
  487. CHttpResponse *forwardResponse = static_cast<CHttpResponse*>(response);
  488. assertex(forwardResponse != nullptr);
  489. StringBuffer errmsg;
  490. if (connect(errmsg, forceNewConnection) < 0)
  491. {
  492. forwardResponse->setContent(errmsg);
  493. forwardResponse->setContentType(HTTP_TYPE_TEXT_PLAIN);
  494. return HttpClientErrCode::Error;
  495. }
  496. Owned<CHttpRequest> httprequest = new CHttpRequest(*m_socket);
  497. Owned<CHttpResponse> httpresponse = new CHttpResponse(*m_socket);
  498. httprequest->setMethod(forwardRequest->queryMethod());
  499. httprequest->setVersion("HTTP/1.1");
  500. if(m_proxy.length() <= 0)
  501. httprequest->setPath(m_path.get());
  502. else
  503. httprequest->setPath(m_url.get());
  504. httprequest->setHost(m_host.get());
  505. httprequest->setPort(m_port);
  506. copyHeaders(*httprequest, *forwardRequest, resetForwardedFor, true);
  507. httprequest->setHeader("HPCC-Forward-For", "true"); //For now limit to one hop, can support multi hpcc forward for hops in future, but why?
  508. StringBuffer contentType;
  509. forwardRequest->getContentType(contentType);
  510. httprequest->setContentType(contentType);
  511. httprequest->setContent(forwardRequest->queryContent());
  512. if (getEspLogLevel()>LogNormal)
  513. {
  514. StringBuffer s;
  515. DBGLOG("Content type: %s", forwardRequest->getContentType(s).str());
  516. DBGLOG("Request content: %s", forwardRequest->queryContent());
  517. }
  518. copyCookies(*httprequest, *forwardRequest, m_host);
  519. httprequest->setPersistentEnabled(!m_disableKeepAlive);
  520. httprequest->send();
  521. if (m_readTimeoutSecs)
  522. httpresponse->setTimeOut(m_readTimeoutSecs);
  523. m_exceptions.setown(MakeMultiException());
  524. int ret = httpresponse->receive(false, m_exceptions);
  525. if (ret < 0 && m_isPersistentSocket && httpresponse->getPeerClosed())
  526. return HttpClientErrCode::PeerClosed;
  527. copyCookies(*forwardResponse, *httpresponse, m_host);
  528. copyHeaders(*forwardResponse, *httpresponse, resetForwardedFor, false);
  529. StringBuffer responseStatus;
  530. StringBuffer responseContentType;
  531. forwardResponse->setStatus(httpresponse->getStatus(responseStatus));
  532. forwardResponse->setContent(httpresponse->queryContent());
  533. forwardResponse->setContentType(httpresponse->getContentType(responseContentType));
  534. m_persistable = httpresponse->getPersistentEligible();
  535. m_numRequests++;
  536. if (getEspLogLevel()>LogNormal)
  537. DBGLOG("Response content: %s", httpresponse->queryContent());
  538. return HttpClientErrCode::OK;
  539. }
  540. int CHttpClient::sendRequest(IProperties *headers, const char* method, const char* contenttype, StringBuffer& content, StringBuffer& responseContent, StringBuffer& responseStatus, bool alwaysReadContent)
  541. {
  542. HttpClientErrCode ret = sendRequest(headers, method, contenttype, content, responseContent, responseStatus, alwaysReadContent, false);
  543. if (ret == HttpClientErrCode::PeerClosed)
  544. ret = sendRequest(headers, method, contenttype, content, responseContent, responseStatus, alwaysReadContent, true);
  545. return static_cast<int>(ret);
  546. }
  547. IHttpMessage *CHttpClient::sendRequestEx(const char* method, const char* contenttype, StringBuffer& content, HttpClientErrCode &code, StringBuffer &errmsg, IProperties *headers, bool alwaysReadContent, bool forceNewConnection)
  548. {
  549. if (connect(errmsg, forceNewConnection) < 0)
  550. {
  551. errmsg.append(errmsg);
  552. code = HttpClientErrCode::Error;
  553. return nullptr;
  554. }
  555. Owned<CHttpRequest> httprequest;
  556. Owned<CHttpResponse> httpresponse;
  557. httprequest.setown(new CHttpRequest(*m_socket));
  558. httpresponse.setown(new CHttpResponse(*m_socket));
  559. if(m_proxy.length() <= 0)
  560. httprequest->setPath(m_path.get());
  561. else
  562. httprequest->setPath(m_url.get());
  563. httprequest->setMethod(method);
  564. httprequest->setVersion("HTTP/1.1");
  565. httprequest->setHost(m_host.get());
  566. httprequest->setPort(m_port);
  567. httprequest->setContentType(contenttype);
  568. bool alreadyEncoded = false;
  569. if (headers)
  570. {
  571. Owned<IPropertyIterator> iter = headers->getIterator();
  572. ForEach(*iter.get())
  573. {
  574. const char *key = iter->getPropKey();
  575. if (key && *key)
  576. {
  577. if (strieq(key, HTTP_HEADER_CONTENT_ENCODING) || strieq(key, HTTP_HEADER_TRANSFER_ENCODING))
  578. alreadyEncoded = true;
  579. const char *value = headers->queryProp(key);
  580. if (value && *value)
  581. httprequest->addHeader(key, value);
  582. }
  583. }
  584. }
  585. if (!alreadyEncoded)
  586. {
  587. httprequest->enableCompression();
  588. httpresponse->enableCompression();
  589. }
  590. if(m_userid.length() > 0)
  591. {
  592. StringBuffer uidpair;
  593. uidpair.append(m_userid.get()).append(":").append(m_password.get());
  594. StringBuffer authhdr("Basic ");
  595. JBASE64_Encode(uidpair.str(), uidpair.length(), authhdr, false);
  596. httprequest->addHeader("Authorization", authhdr.str());
  597. }
  598. if(m_realm.length() > 0)
  599. {
  600. StringBuffer authheader;
  601. authheader.append("Basic realm=\"").append(m_realm).append("\"");
  602. httprequest->addHeader("WWW-Authenticate", authheader.str());
  603. }
  604. if (getEspLogLevel()>LogNormal)
  605. {
  606. DBGLOG("Content type: %s", contenttype);
  607. DBGLOG("Request content: %s", content.str());
  608. }
  609. httprequest->setContent(content.str());
  610. //httprequest->sendWithoutContentType();
  611. #ifdef COOKIE_HANDLING
  612. ForEachItemIn(x, m_request_cookies)
  613. {
  614. CEspCookie* cookie = &m_request_cookies.item(x);
  615. if(cookie)
  616. httprequest->addCookie(LINK(cookie));
  617. }
  618. #endif
  619. httprequest->setPersistentEnabled(!m_disableKeepAlive);
  620. httprequest->send();
  621. if (m_readTimeoutSecs)
  622. httpresponse->setTimeOut(m_readTimeoutSecs);
  623. m_exceptions.setown(MakeMultiException());
  624. int ret = httpresponse->receive(alwaysReadContent, m_exceptions);
  625. if (ret < 0 && m_isPersistentSocket && httpresponse->getPeerClosed())
  626. {
  627. code = HttpClientErrCode::PeerClosed;
  628. return nullptr;
  629. }
  630. #ifdef COOKIE_HANDLING
  631. if(m_context)
  632. {
  633. IArrayOf<CEspCookie>& cookies = httpresponse->queryCookies();
  634. ForEachItemIn(x, cookies)
  635. {
  636. CEspCookie* cookie = &cookies.item(x);
  637. if(!cookie)
  638. continue;
  639. cookie->setHost(m_host.get());
  640. m_context->addCookie(cookie);
  641. }
  642. }
  643. #endif
  644. m_persistable = httpresponse->getPersistentEligible();
  645. m_numRequests++;
  646. code = HttpClientErrCode::OK;
  647. return httpresponse.getClear();
  648. }
  649. HttpClientErrCode CHttpClient::sendRequest(IProperties *headers, const char* method, const char* contenttype, StringBuffer& content, StringBuffer& responseContent, StringBuffer& responseStatus, bool alwaysReadContent, bool forceNewConnection)
  650. {
  651. HttpClientErrCode code = HttpClientErrCode::OK;
  652. Owned<IHttpMessage> resp = sendRequestEx(method, contenttype, content, code, responseContent, headers, alwaysReadContent, forceNewConnection);
  653. if (!resp || code != HttpClientErrCode::OK)
  654. return code;
  655. resp->getContent(responseContent);
  656. resp->getStatus(responseStatus);
  657. if (getEspLogLevel()>LogNormal)
  658. DBGLOG("Response content: %s", responseContent.str());
  659. return code;
  660. }
  661. int CHttpClient::sendRequest(const char* method, const char* contenttype, StringBuffer& request, StringBuffer& response, StringBuffer& responseStatus, bool alwaysReadContent)
  662. {
  663. return sendRequest(NULL, method, contenttype, request, response, responseStatus, alwaysReadContent);
  664. }
  665. // since an element may have namespace specified in its tag, don't look for trailing '>'
  666. // in its start tag
  667. static const char* getElementText(const char* str, const char* beginTag/*like '<A'*/, const char* endTag/*like '</A>'*/,
  668. int& textLen)
  669. {
  670. const char* element = strstr(str, beginTag);//element points to '<A...'
  671. if (element)
  672. {
  673. const char* endStartTag = strchr(element, '>');/* > of start tag <A...>*/
  674. const char* beginEndTag = strstr(element, endTag);
  675. if (endStartTag && beginEndTag && endStartTag++ < beginEndTag)
  676. {
  677. textLen = beginEndTag - endStartTag;
  678. return endStartTag;
  679. }
  680. }
  681. textLen = 0;
  682. return NULL;
  683. }
  684. static void parseSoapFault(const char* content, StringBuffer& msg)
  685. {
  686. const char* start = strstr(content, ":Fault");//match any namespace like 'soap' or 'soapenv' etc. before :Fault
  687. if (start)
  688. start = strchr(start, '>');
  689. if (start)
  690. {
  691. start += 1;
  692. msg.append("SOAP fault:");
  693. int textLen;
  694. const char* elementText;
  695. elementText = getElementText(start, "<faultcode", "</faultcode>", textLen);
  696. if (elementText)
  697. {
  698. msg.append(" code=");
  699. msg.append(textLen, elementText);
  700. msg.append(".");
  701. }
  702. elementText = getElementText(start, "<faultstring", "</faultstring>", textLen);
  703. if (elementText)
  704. {
  705. msg.append(" string=");
  706. msg.append(textLen, elementText);
  707. msg.append(".");
  708. }
  709. elementText = getElementText(start, "<detail", "</detail>", textLen);
  710. if (elementText)
  711. {
  712. msg.append(" detail=");
  713. msg.append(textLen, elementText);
  714. msg.append(".");
  715. }
  716. }
  717. }
  718. int CHttpClient::postRequest(ISoapMessage &req, ISoapMessage& resp)
  719. {
  720. HttpClientErrCode ret = postRequest(req, resp, false);
  721. if (ret == HttpClientErrCode::PeerClosed)
  722. ret = postRequest(req, resp, true);
  723. return static_cast<int>(ret);
  724. }
  725. HttpClientErrCode CHttpClient::postRequest(ISoapMessage &req, ISoapMessage& resp, bool forceNewConnection)
  726. {
  727. CSoapRequest& request = *(dynamic_cast<CSoapRequest*>(&req));
  728. const char* requeststr = request.get_text();
  729. CSoapResponse& response = *(dynamic_cast<CSoapResponse*>(&resp));
  730. StringBuffer errmsg;
  731. if (connect(errmsg, forceNewConnection) < 0 || !m_socket)
  732. {
  733. response.set_status(SOAP_CONNECTION_ERROR);
  734. response.set_err(errmsg);
  735. return HttpClientErrCode::Error;
  736. }
  737. Owned<CHttpRequest> httprequest(new CHttpRequest(*m_socket));
  738. Owned<CHttpResponse> httpresponse(new CHttpResponse(*m_socket));
  739. httprequest->enableCompression();
  740. httpresponse->enableCompression();
  741. httprequest->setMethod("POST");
  742. httprequest->setVersion("HTTP/1.1");
  743. if(m_proxy.length() <= 0)
  744. {
  745. httprequest->setPath(m_path.get());
  746. }
  747. else
  748. {
  749. httprequest->setPath(m_url.get());
  750. }
  751. httprequest->setHost(m_host.get());
  752. httprequest->setPort(m_port);
  753. if(strlen(request.get_content_type()) > 0)
  754. httprequest->setContentType(request.get_content_type());
  755. const char* soapaction = request.get_soapaction();
  756. if(soapaction != NULL && strlen(soapaction) > 0)
  757. {
  758. httprequest->addHeader("SOAPAction", soapaction);
  759. }
  760. if(m_userid.length() > 0)
  761. {
  762. StringBuffer uidpair;
  763. uidpair.append(m_userid.get()).append(":").append(m_password.get());
  764. StringBuffer authhdr("Basic ");
  765. JBASE64_Encode(uidpair.str(), uidpair.length(), authhdr, false);
  766. httprequest->addHeader("Authorization", authhdr.str());
  767. if(m_proxy.length())
  768. httprequest->addHeader("Proxy-Authorization", authhdr.str());
  769. }
  770. if(m_realm.length() > 0)
  771. {
  772. StringBuffer authheader;
  773. authheader.append("Basic realm=\"").append(m_realm).append("\"");
  774. httprequest->addHeader("WWW-Authenticate", authheader.str());
  775. }
  776. if (m_disableKeepAlive)
  777. httprequest->addHeader("Connection", "close");
  778. httprequest->setContentType(HTTP_TYPE_TEXT_XML);
  779. httprequest->setContent(requeststr);
  780. #ifdef COOKIE_HANDLING
  781. ForEachItemIn(x, m_request_cookies)
  782. {
  783. CEspCookie* cookie = &m_request_cookies.item(x);
  784. if(cookie)
  785. httprequest->addCookie(LINK(cookie));
  786. }
  787. #endif
  788. httprequest->send();
  789. if (m_readTimeoutSecs)
  790. httpresponse->setTimeOut(m_readTimeoutSecs);
  791. m_exceptions.setown(MakeMultiException());
  792. int ret = httpresponse->receive(true, m_exceptions);
  793. if (ret < 0 && m_isPersistentSocket && httpresponse->getPeerClosed())
  794. return HttpClientErrCode::PeerClosed;
  795. #ifdef COOKIE_HANDLING
  796. if(m_context)
  797. {
  798. IArrayOf<CEspCookie>& cookies = httpresponse->queryCookies();
  799. ForEachItemIn(x, cookies)
  800. {
  801. CEspCookie* cookie = &cookies.item(x);
  802. if(!cookie)
  803. continue;
  804. cookie->setHost(m_host.get());
  805. m_context->addCookie(cookie);
  806. }
  807. }
  808. #endif
  809. StringBuffer status;
  810. httpresponse->getStatus(status);
  811. int statusCode = atoi(status.str());
  812. char statusClass = '0';
  813. if(status.length() > 0)
  814. statusClass = status.charAt(0);
  815. errmsg.clear().appendf("HTTP Status %s", status.str());
  816. if(statusClass == '2')
  817. {
  818. response.set_status(SOAP_OK);
  819. }
  820. else if(statusClass == '3')
  821. {
  822. response.set_status(SOAP_SERVER_ERROR);
  823. response.set_err(errmsg.str());
  824. return HttpClientErrCode::Error;
  825. }
  826. else if(statusClass == '4')
  827. {
  828. if(statusCode == HTTP_STATUS_UNAUTHORIZED_CODE ||
  829. statusCode == HTTP_STATUS_FORBIDDEN_CODE ||
  830. statusCode == HTTP_STATUS_NOT_ALLOWED_CODE ||
  831. statusCode == HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED_CODE)
  832. response.set_status(SOAP_AUTHENTICATION_ERROR);
  833. else
  834. response.set_status(SOAP_CLIENT_ERROR);
  835. response.set_err(errmsg.str());
  836. DBGLOG("SOAP_CLIENT_ERROR: %s", errmsg.str());
  837. return HttpClientErrCode::Error;
  838. }
  839. else if(statusClass == '5')
  840. {
  841. response.set_status(SOAP_SERVER_ERROR);
  842. StringBuffer content;
  843. parseSoapFault(httpresponse->getContent(content),errmsg);
  844. response.set_err(errmsg.str());
  845. #ifdef _DEBUG // JCSMORE - check with AF
  846. DBGLOG("SOAP_SERVER_ERROR: %s", errmsg.str());
  847. #endif
  848. return HttpClientErrCode::Error;
  849. }
  850. else
  851. {
  852. DBGLOG("%s", errmsg.str());
  853. StringBuffer msg;
  854. if (m_exceptions->ordinality())
  855. {
  856. aindex_t count = m_exceptions->ordinality();
  857. for (aindex_t i = 0; i < count; i++)
  858. {
  859. IException& ex = m_exceptions->item(i);
  860. int errCode = ex.errorCode();
  861. StringBuffer buf;
  862. msg.appendf("errorCode = %d\t message = %s\n", errCode, ex.errorMessage(buf).str());
  863. }
  864. }
  865. DBGLOG("SOAP_RPC_ERROR = %s", msg.str());
  866. response.set_status(SOAP_RPC_ERROR);
  867. response.set_err(msg);
  868. return HttpClientErrCode::Error;
  869. }
  870. m_persistable = httpresponse->getPersistentEligible();
  871. m_numRequests++;
  872. StringBuffer contenttype;
  873. httpresponse->getContentType(contenttype);
  874. response.set_content_type(contenttype.str());
  875. StringBuffer content;
  876. httpresponse->getContent(content);
  877. if (getEspLogLevel()>LogNormal)
  878. {
  879. if(httpresponse->isTextMessage())
  880. DBGLOG("http response content = %s", content.str());
  881. }
  882. response.set_text(content.str());
  883. // parse soap fault
  884. parseSoapFault(content,errmsg.clear());
  885. if (errmsg.length())
  886. response.set_err(errmsg);
  887. return HttpClientErrCode::OK;
  888. }
  889. IHttpClientContext* getHttpClientContext()
  890. {
  891. CriticalBlock b(httpCrit);
  892. if(theHttpClientContext.get() == NULL)
  893. {
  894. theHttpClientContext.setown(new CHttpClientContext());
  895. }
  896. return theHttpClientContext.getLink();
  897. }
  898. IHttpClientContext* getHttpClientSecretContext(const char *secret)
  899. {
  900. if (isEmptyString(secret))
  901. return getHttpClientContext();
  902. else
  903. {
  904. CriticalBlock b(httpCrit);
  905. CHttpClientContext *ctx = httpClientContextsUsingSecrets.getValue(secret);
  906. if(ctx == NULL)
  907. {
  908. Owned<CHttpClientContext> newctx = new CHttpClientContext();
  909. newctx->setMtlsSecretName(secret);
  910. httpClientContextsUsingSecrets.setValue(secret, newctx.getLink());
  911. return newctx.getClear();
  912. }
  913. return LINK(ctx);
  914. }
  915. }
  916. IHttpClientContext* createHttpClientContext(IPropertyTree* config)
  917. {
  918. return new CHttpClientContext(config);
  919. }