soapclient.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  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. #include "esphttp.hpp"
  15. #include <stdlib.h>
  16. //ESP Bindings
  17. #include "espcontext.hpp"
  18. #include "http/client/httpclient.hpp"
  19. #include "SOAP/client/soapclient.hpp"
  20. #include "bindutil.hpp"
  21. #include <memory>
  22. int CSoapClient::setUsernameToken(const char* username, const char* password, const char* realm)
  23. {
  24. m_username.set(username);
  25. m_password.set(password);
  26. m_realm.set(realm);
  27. return 0;
  28. }
  29. // Under most cases, use this one
  30. int CSoapClient::postRequest(IRpcMessage& rpccall, IRpcMessage& rpcresponse)
  31. {
  32. return postRequest(NULL, NULL, rpccall, rpcresponse);
  33. }
  34. // If you want to set a soapaction http header, use this one.
  35. int CSoapClient::postRequest(const char* soapaction, IRpcMessage& rpccall, StringBuffer& resp)
  36. {
  37. return postRequest(NULL, soapaction, rpccall, resp);
  38. }
  39. // If you want to set a soapaction http header, use this one.
  40. int CSoapClient::postRequest(const char* soapaction, IRpcMessage& rpccall, StringBuffer& resp, IRpcMessageArray *headers)
  41. {
  42. return postRequest(NULL, soapaction, rpccall, resp, headers);
  43. }
  44. int CSoapClient::postRequest(const char* soapaction, IRpcMessage& rpccall, IRpcMessage& rpcresponse)
  45. {
  46. return postRequest(NULL, soapaction, rpccall, rpcresponse);
  47. }
  48. // If you want a content-type that is different from "soap/application", use this one. This should not be necessary
  49. // unless the server side doesn't follow the standard.
  50. int CSoapClient::postRequest(const char* contenttype, const char* soapaction, IRpcMessage& rpccall, StringBuffer& responsebuf)
  51. {
  52. return postRequest(contenttype, soapaction, rpccall, responsebuf, NULL, NULL);
  53. }
  54. int CSoapClient::postRequest(const char* contenttype, const char* soapaction, IRpcMessage& rpccall, StringBuffer& responsebuf, IRpcMessageArray *headers)
  55. {
  56. return postRequest(contenttype, soapaction, rpccall, responsebuf, NULL, headers);
  57. }
  58. int CSoapClient::postRequest(const char* contenttype, const char* soapaction, IRpcMessage& rpccall, StringBuffer& responsebuf, CMimeMultiPart* resp_multipart, IRpcMessageArray *headers)
  59. {
  60. CRpcCall* call = (CRpcCall *)&rpccall;
  61. CBody* req_body = new CBody;
  62. req_body->add_rpcmessage(call);
  63. CHeader* req_header = NULL;
  64. if(m_username.length() > 0)
  65. {
  66. req_header = new CHeader;
  67. IRpcMessage* oneblock = new CRpcMessage("Security");
  68. oneblock->set_ns("wsse");
  69. oneblock->add_value("", "wsse", "UsernameToken", "", "");
  70. oneblock->add_value("UsernameToken", "wsse", "Username", "", m_username.get());
  71. oneblock->add_value("UsernameToken", "wsse", "Password", "", m_password.get());
  72. oneblock->add_value("RealmToken", "wsse", "Realm", "", m_realm.get());
  73. req_header->addHeaderBlock(oneblock);
  74. }
  75. if (headers)
  76. {
  77. if (!req_header)
  78. req_header = new CHeader;
  79. ForEachItemIn(idx, *headers)
  80. {
  81. req_header->addHeaderBlock(&headers->item(idx));
  82. headers->remove(idx, true);
  83. }
  84. }
  85. // Form request
  86. Owned<CEnvelope> req_envelope;
  87. req_envelope.setown(new CEnvelope(req_header, req_body));
  88. Owned<CMimeMultiPart> multipart;
  89. multipart.setown(new CMimeMultiPart("1.0", "", "MIME_boundary", "text/xml", "soaproot"));
  90. req_envelope->marshall(multipart);
  91. StringBuffer requeststr;
  92. StringBuffer contenttypestr;
  93. if(multipart->getBodyCount() == 1)
  94. {
  95. CMimeBodyPart* rootpart = multipart->queryRootPart();
  96. rootpart->getContent(requeststr);
  97. }
  98. else
  99. {
  100. multipart->serialize(contenttypestr, requeststr);
  101. }
  102. if (getEspLogLevel(rpccall.queryContext())>LogNormal)
  103. {
  104. DBGLOG("Content type: %s", contenttypestr.str());
  105. }
  106. Owned<CSoapRequest> soap_request;
  107. soap_request.setown(new CSoapRequest);
  108. //Use the provided contenttype to overwrite the default content type.
  109. if(contenttype != NULL && strlen(contenttype) > 0)
  110. soap_request->set_content_type(contenttype);
  111. else
  112. soap_request->set_content_type(contenttypestr.str());
  113. soap_request->set_text(requeststr.str());
  114. if(soapaction != NULL && strlen(soapaction) > 0)
  115. soap_request->set_soapaction(soapaction);
  116. Owned<CSoapResponse> soap_response;
  117. soap_response.setown(new CSoapResponse);
  118. //Send request and get response
  119. if(m_transportclient.get() == NULL)
  120. {
  121. Owned<IHttpClientContext> httpctx = getHttpClientContext();
  122. Owned<IHttpClient> httpclient = httpctx->createHttpClient(call->getProxy(), call->get_url());
  123. if (m_disableKeepAlive)
  124. httpclient->disableKeepAlive();
  125. if(m_username.length() > 0)
  126. {
  127. httpclient->setUserID(m_username.get());
  128. httpclient->setPassword(m_password.get());
  129. }
  130. if(m_realm.length() > 0)
  131. {
  132. httpclient->setRealm(m_realm.get());
  133. }
  134. if (m_connectTimeoutMs)
  135. httpclient->setConnectTimeOutMs(m_connectTimeoutMs);
  136. if (m_readTimeoutSecs)
  137. httpclient->setTimeOut(m_readTimeoutSecs);
  138. m_transportclient.setown(httpclient.getClear());
  139. }
  140. if (!soap_request.get() || !soap_response.get())
  141. throw MakeStringException(-1, "request or response is NULL");
  142. m_transportclient->postRequest(*soap_request.get(), *soap_response.get());
  143. int retstatus = soap_response->get_status();
  144. if(retstatus != SOAP_OK)
  145. {
  146. StringBuffer errmsg = "SOAP error";
  147. if(retstatus == SOAP_CLIENT_ERROR)
  148. errmsg = "SOAP client error";
  149. else if(retstatus == SOAP_SERVER_ERROR)
  150. errmsg = "SOAP server error";
  151. else if(retstatus == SOAP_RPC_ERROR)
  152. errmsg = "SOAP rpc error";
  153. else if(retstatus == SOAP_CONNECTION_ERROR)
  154. errmsg = "SOAP Connection error";
  155. else if(retstatus == SOAP_REQUEST_TYPE_ERROR)
  156. errmsg = "SOAP request type error";
  157. else if(retstatus == SOAP_AUTHENTICATION_ERROR)
  158. errmsg = "SOAP authentication error";
  159. const char *errText = soap_response->get_err();
  160. if (errText && *errText)
  161. {
  162. if (retstatus == SOAP_SERVER_ERROR) //check for special case of VERY ugly soapfault string
  163. {
  164. const char *faultString = strstr(errText, "string=[");
  165. if (faultString)
  166. {
  167. faultString += 8;
  168. const char *endString = strchr(faultString, ']');
  169. if (endString)
  170. {
  171. errmsg.clear().append(endString-faultString, faultString);
  172. errText = nullptr;
  173. }
  174. }
  175. }
  176. if (errText)
  177. errmsg.appendf("[%s]", errText);
  178. }
  179. throw MakeStringException(retstatus, "%s", errmsg.str());
  180. }
  181. #if defined(DEBUG_HTTP_)
  182. DBGLOG("response content type = %s", soap_response->get_content_type());
  183. #endif
  184. if(!Utils::strncasecmp(soap_response->get_content_type(), "application/soap", strlen("application/soap")) || !Utils::strncasecmp(soap_response->get_content_type(), "text/xml", strlen("text/xml")))
  185. {
  186. responsebuf.append(soap_response->get_text());
  187. }
  188. else if(!Utils::strncasecmp(soap_response->get_content_type(), "Multipart/Related", strlen("Multipart/Related")))
  189. {
  190. CMimeMultiPart* parts = resp_multipart ? resp_multipart : new CMimeMultiPart("1.0", "Multipart/Related", "", "", "");
  191. parts->unserialize(soap_response->get_content_type(), soap_response->get_text_length(), soap_response->get_text());
  192. CMimeBodyPart* rootpart = parts->queryRootPart();
  193. rootpart->getContent(responsebuf);
  194. if (!resp_multipart)
  195. delete parts;
  196. }
  197. else
  198. {
  199. DBGLOG("SOAP Response type %s not supported", soap_response->get_content_type());
  200. return SOAP_REQUEST_TYPE_ERROR;
  201. }
  202. if(responsebuf.length() == 0)
  203. {
  204. throw MakeStringException(-1, "Empty SOAP message received");
  205. }
  206. return SOAP_OK;
  207. }
  208. int CSoapClient::postRequest(const char* contenttype, const char* soapaction, IRpcMessage& rpccall, IRpcMessage& rpcresponse)
  209. {
  210. StringBuffer responsebuf;
  211. Owned<CMimeMultiPart> parts = new CMimeMultiPart("1.0", "Multipart/Related", "", "", "");
  212. int rt = postRequest(contenttype, soapaction, rpccall, responsebuf, parts);
  213. if (rt != SOAP_OK)
  214. return rt;
  215. // DBGLOG("response SoapClient got from soap server = \n%s", responsebuf.str());
  216. std::unique_ptr<XmlPullParser> xpp(new XmlPullParser());
  217. int bufSize = responsebuf.length();
  218. xpp->setSupportNamespaces(true);
  219. xpp->setInput(responsebuf.str(), bufSize);
  220. int type;
  221. StartTag stag;
  222. EndTag etag;
  223. Owned<CEnvelope> res_envelope;
  224. res_envelope.setown(new CEnvelope);
  225. while((type = xpp->next()) != XmlPullParser::END_DOCUMENT)
  226. {
  227. if(type == XmlPullParser::START_TAG) {
  228. xpp->readStartTag(stag);
  229. if(!stricmp(stag.getLocalName(), SOAP_ENVELOPE_NAME))
  230. {
  231. res_envelope->unmarshall(xpp.get());
  232. break;
  233. }
  234. }
  235. }
  236. CBody* res_body = res_envelope->get_body();
  237. CRpcResponse* response = (CRpcResponse *)&rpcresponse;
  238. res_body->nextRpcMessage(response);
  239. response->unmarshall(xpp.get(), parts.get());
  240. return SOAP_OK;
  241. }
  242. int CSoapClient::postRequest(IRpcMessage & rpccall, StringBuffer & responsebuf)
  243. {
  244. return postRequest(rpccall, responsebuf, NULL);
  245. }
  246. int CSoapClient::postRequest(IRpcMessage & rpccall, StringBuffer & responsebuf, IRpcMessageArray *headers)
  247. {
  248. CRpcCall* call = (CRpcCall *)&rpccall;
  249. CBody* req_body = new CBody;
  250. req_body->add_rpcmessage(call);
  251. CHeader* req_header = NULL;
  252. if(m_username.length() > 0)
  253. {
  254. req_header = new CHeader;
  255. IRpcMessage* oneblock = new CRpcMessage("Security");
  256. oneblock->set_ns("wsse");
  257. oneblock->add_value("", "wsse", "UsernameToken", "", "");
  258. oneblock->add_value("UsernameToken", "wsse", "Username", "", m_username.get());
  259. oneblock->add_value("UsernameToken", "wsse", "Password", "", m_password.get());
  260. oneblock->add_value("RealmToken", "wsse", "Realm", "", m_realm.get());
  261. req_header->addHeaderBlock(oneblock);
  262. }
  263. if (headers)
  264. {
  265. if (!req_header)
  266. req_header = new CHeader;
  267. ForEachItemIn(idx, *headers)
  268. {
  269. req_header->addHeaderBlock(&headers->item(idx));
  270. headers->remove(idx, true);
  271. }
  272. }
  273. // Form request
  274. Owned<CEnvelope> req_envelope;
  275. req_envelope.setown(new CEnvelope(req_header, req_body));
  276. Owned<CMimeMultiPart> multipart;
  277. multipart.setown(new CMimeMultiPart("1.0", "", "MIME_boundary", "text/xml", "soaproot"));
  278. req_envelope->marshall(multipart);
  279. StringBuffer requeststr;
  280. StringBuffer contenttypestr;
  281. if(multipart->getBodyCount() == 1)
  282. {
  283. CMimeBodyPart* rootpart = multipart->queryRootPart();
  284. rootpart->getContent(requeststr);
  285. }
  286. else
  287. {
  288. multipart->serialize(contenttypestr, requeststr);
  289. }
  290. Owned<CSoapRequest> soap_request = new CSoapRequest();
  291. soap_request->set_content_type(contenttypestr.str());
  292. soap_request->set_text(requeststr.str());
  293. #if defined(DEBUG_HTTP_)
  294. DBGLOG("SOAP Request content: %s", requeststr.str());
  295. #endif
  296. Owned<CSoapResponse> soap_response = new CSoapResponse();
  297. //Send request and get response
  298. if(m_transportclient.get() == NULL)
  299. {
  300. Owned<IHttpClientContext> httpctx = getHttpClientContext();
  301. IHttpClient* httpclient = httpctx->createHttpClient(call->getProxy(), call->get_url());
  302. m_transportclient.setown(httpclient);
  303. if (m_disableKeepAlive)
  304. httpclient->disableKeepAlive();
  305. if(m_username.length() > 0)
  306. {
  307. httpclient->setUserID(m_username.get());
  308. httpclient->setPassword(m_password.get());
  309. }
  310. if(m_realm.length() > 0)
  311. {
  312. httpclient->setRealm(m_realm.get());
  313. }
  314. }
  315. if (!soap_request.get() || !soap_response.get())
  316. throw MakeStringException(-1, "request or response is NULL");
  317. m_transportclient->postRequest(*soap_request.get(), *soap_response.get());
  318. int retstatus = soap_response->get_status();
  319. if(retstatus != SOAP_OK)
  320. {
  321. const char* errmsg = "SOAP error";
  322. if(retstatus == SOAP_CLIENT_ERROR)
  323. errmsg = "SOAP client error";
  324. else if(retstatus == SOAP_SERVER_ERROR)
  325. errmsg = "SOAP server error";
  326. else if(retstatus == SOAP_RPC_ERROR)
  327. errmsg = "SOAP rpc error";
  328. else if(retstatus == SOAP_CONNECTION_ERROR)
  329. errmsg = "SOAP Connection error";
  330. else if(retstatus == SOAP_REQUEST_TYPE_ERROR)
  331. errmsg = "SOAP request type error";
  332. else if(retstatus == SOAP_AUTHENTICATION_ERROR)
  333. errmsg = "SOAP authentication error";
  334. throw MakeStringException(retstatus, "%s", errmsg);
  335. }
  336. if(soap_response->get_text_length() == 0)
  337. {
  338. throw MakeStringException(-1, "Empty SOAP message received");
  339. }
  340. StringBuffer& resptext = soap_response->query_text();
  341. responsebuf.swapWith(resptext);
  342. return SOAP_OK;
  343. }