httpclient.cpp 31 KB

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