httpclient.cpp 29 KB

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