soapclient.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /*##############################################################################
  2. Copyright (C) 2011 HPCC Systems.
  3. All rights reserved. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ############################################################################## */
  14. #pragma warning( disable : 4786)
  15. #ifdef WIN32
  16. #ifdef ESPHTTP_EXPORTS
  17. #define esp_http_decl __declspec(dllexport)
  18. #endif
  19. #endif
  20. #include <stdlib.h>
  21. //ESP Bindings
  22. #include "espcontext.hpp"
  23. #include "http/client/httpclient.hpp"
  24. #include "SOAP/client/soapclient.hpp"
  25. #include "bindutil.hpp"
  26. #include <memory>
  27. int CSoapClient::setUsernameToken(const char* username, const char* password, const char* realm)
  28. {
  29. m_username.set(username);
  30. m_password.set(password);
  31. m_realm.set(realm);
  32. return 0;
  33. }
  34. // Under most cases, use this one
  35. int CSoapClient::postRequest(IRpcMessage& rpccall, IRpcMessage& rpcresponse)
  36. {
  37. return postRequest(NULL, NULL, rpccall, rpcresponse);
  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)
  41. {
  42. return postRequest(NULL, soapaction, rpccall, resp);
  43. }
  44. // If you want to set a soapaction http header, use this one.
  45. int CSoapClient::postRequest(const char* soapaction, IRpcMessage& rpccall, StringBuffer& resp, IRpcMessageArray *headers)
  46. {
  47. return postRequest(NULL, soapaction, rpccall, resp, headers);
  48. }
  49. int CSoapClient::postRequest(const char* soapaction, IRpcMessage& rpccall, IRpcMessage& rpcresponse)
  50. {
  51. return postRequest(NULL, soapaction, rpccall, rpcresponse);
  52. }
  53. // If you want a content-type that is different from "soap/application", use this one. This should not be necessary
  54. // unless the server side doesn't follow the standard.
  55. int CSoapClient::postRequest(const char* contenttype, const char* soapaction, IRpcMessage& rpccall, StringBuffer& responsebuf)
  56. {
  57. return postRequest(contenttype, soapaction, rpccall, responsebuf, NULL, NULL);
  58. }
  59. int CSoapClient::postRequest(const char* contenttype, const char* soapaction, IRpcMessage& rpccall, StringBuffer& responsebuf, IRpcMessageArray *headers)
  60. {
  61. return postRequest(contenttype, soapaction, rpccall, responsebuf, NULL, headers);
  62. }
  63. int CSoapClient::postRequest(const char* contenttype, const char* soapaction, IRpcMessage& rpccall, StringBuffer& responsebuf, CMimeMultiPart* resp_multipart, IRpcMessageArray *headers)
  64. {
  65. CRpcCall* call = (CRpcCall *)&rpccall;
  66. CBody* req_body = new CBody;
  67. req_body->add_rpcmessage(call);
  68. CHeader* req_header = NULL;
  69. if(m_username.length() > 0)
  70. {
  71. req_header = new CHeader;
  72. IRpcMessage* oneblock = new CRpcMessage("Security");
  73. oneblock->set_ns("wsse");
  74. oneblock->add_value("", "wsse", "UsernameToken", "", "");
  75. oneblock->add_value("UsernameToken", "wsse", "Username", "", m_username.get());
  76. oneblock->add_value("UsernameToken", "wsse", "Password", "", m_password.get());
  77. oneblock->add_value("RealmToken", "wsse", "Realm", "", m_realm.get());
  78. req_header->addHeaderBlock(oneblock);
  79. }
  80. if (headers)
  81. {
  82. if (!req_header)
  83. req_header = new CHeader;
  84. ForEachItemIn(idx, *headers)
  85. {
  86. req_header->addHeaderBlock(&headers->item(idx));
  87. headers->remove(idx, true);
  88. }
  89. }
  90. // Form request
  91. Owned<CEnvelope> req_envelope;
  92. req_envelope.setown(new CEnvelope(req_header, req_body));
  93. Owned<CMimeMultiPart> multipart;
  94. multipart.setown(new CMimeMultiPart("1.0", "", "MIME_boundary", "text/xml", "soaproot"));
  95. req_envelope->marshall(multipart);
  96. StringBuffer requeststr;
  97. StringBuffer contenttypestr;
  98. if(multipart->getBodyCount() == 1)
  99. {
  100. CMimeBodyPart* rootpart = multipart->queryRootPart();
  101. rootpart->getContent(requeststr);
  102. }
  103. else
  104. {
  105. multipart->serialize(contenttypestr, requeststr);
  106. }
  107. if (getEspLogLevel(rpccall.queryContext())>LogNormal)
  108. {
  109. DBGLOG("Content type: %s", contenttypestr.str());
  110. DBGLOG("Request content: %s", requeststr.str());
  111. }
  112. Owned<CSoapRequest> soap_request;
  113. soap_request.setown(new CSoapRequest);
  114. //Use the provided contenttype to overwrite the default content type.
  115. if(contenttype != NULL && strlen(contenttype) > 0)
  116. soap_request->set_content_type(contenttype);
  117. else
  118. soap_request->set_content_type(contenttypestr.str());
  119. soap_request->set_text(requeststr.str());
  120. if(soapaction != NULL && strlen(soapaction) > 0)
  121. soap_request->set_soapaction(soapaction);
  122. Owned<CSoapResponse> soap_response;
  123. soap_response.setown(new CSoapResponse);
  124. //Send request and get response
  125. if(m_transportclient.get() == NULL)
  126. {
  127. Owned<IHttpClientContext> httpctx = getHttpClientContext();
  128. IHttpClient* httpclient = httpctx->createHttpClient(call->getProxy(), call->get_url());
  129. m_transportclient.setown(httpclient);
  130. if (m_disableKeepAlive)
  131. httpclient->disableKeepAlive();
  132. if(m_username.length() > 0)
  133. {
  134. httpclient->setUserID(m_username.get());
  135. httpclient->setPassword(m_password.get());
  136. }
  137. if(m_realm.length() > 0)
  138. {
  139. httpclient->setRealm(m_realm.get());
  140. }
  141. }
  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. errmsg.appendf("[%s]", errText);
  162. throw MakeStringException(retstatus, "%s", errmsg.str());
  163. }
  164. #if defined(DEBUG_HTTP_)
  165. DBGLOG("response content type = %s", soap_response->get_content_type());
  166. #endif
  167. 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")))
  168. {
  169. responsebuf.append(soap_response->get_text());
  170. }
  171. else if(!Utils::strncasecmp(soap_response->get_content_type(), "Multipart/Related", strlen("Multipart/Related")))
  172. {
  173. CMimeMultiPart* parts = resp_multipart ? resp_multipart : new CMimeMultiPart("1.0", "Multipart/Related", "", "", "");
  174. parts->unserialize(soap_response->get_content_type(), soap_response->get_text_length(), soap_response->get_text());
  175. CMimeBodyPart* rootpart = parts->queryRootPart();
  176. rootpart->getContent(responsebuf);
  177. if (!resp_multipart)
  178. delete parts;
  179. }
  180. else
  181. {
  182. DBGLOG("SOAP Response type %s not supported", soap_response->get_content_type());
  183. return SOAP_REQUEST_TYPE_ERROR;
  184. }
  185. if(responsebuf.length() == 0)
  186. {
  187. throw MakeStringException(-1, "Empty SOAP message received");
  188. }
  189. return SOAP_OK;
  190. }
  191. int CSoapClient::postRequest(const char* contenttype, const char* soapaction, IRpcMessage& rpccall, IRpcMessage& rpcresponse)
  192. {
  193. StringBuffer responsebuf;
  194. Owned<CMimeMultiPart> parts = new CMimeMultiPart("1.0", "Multipart/Related", "", "", "");
  195. int rt = postRequest(contenttype, soapaction, rpccall, responsebuf, parts);
  196. if (rt != SOAP_OK)
  197. return rt;
  198. // DBGLOG("response SoapClient got from soap server = \n%s", responsebuf.str());
  199. auto_ptr<XmlPullParser> xpp(new XmlPullParser());
  200. int bufSize = responsebuf.length();
  201. xpp->setSupportNamespaces(true);
  202. xpp->setInput(responsebuf.str(), bufSize);
  203. int type;
  204. StartTag stag;
  205. EndTag etag;
  206. Owned<CEnvelope> res_envelope;
  207. res_envelope.setown(new CEnvelope);
  208. while((type = xpp->next()) != XmlPullParser::END_DOCUMENT)
  209. {
  210. if(type == XmlPullParser::START_TAG) {
  211. xpp->readStartTag(stag);
  212. if(!stricmp(stag.getLocalName(), SOAP_ENVELOPE_NAME))
  213. {
  214. res_envelope->unmarshall(xpp.get());
  215. break;
  216. }
  217. }
  218. }
  219. CBody* res_body = res_envelope->get_body();
  220. CRpcResponse* response = (CRpcResponse *)&rpcresponse;
  221. res_body->nextRpcMessage(response);
  222. response->unmarshall(xpp.get(), parts.get());
  223. return SOAP_OK;
  224. }
  225. int CSoapClient::postRequest(IRpcMessage & rpccall, StringBuffer & responsebuf)
  226. {
  227. return postRequest(rpccall, responsebuf, NULL);
  228. }
  229. int CSoapClient::postRequest(IRpcMessage & rpccall, StringBuffer & responsebuf, IRpcMessageArray *headers)
  230. {
  231. CRpcCall* call = (CRpcCall *)&rpccall;
  232. CBody* req_body = new CBody;
  233. req_body->add_rpcmessage(call);
  234. CHeader* req_header = NULL;
  235. if(m_username.length() > 0)
  236. {
  237. req_header = new CHeader;
  238. IRpcMessage* oneblock = new CRpcMessage("Security");
  239. oneblock->set_ns("wsse");
  240. oneblock->add_value("", "wsse", "UsernameToken", "", "");
  241. oneblock->add_value("UsernameToken", "wsse", "Username", "", m_username.get());
  242. oneblock->add_value("UsernameToken", "wsse", "Password", "", m_password.get());
  243. oneblock->add_value("RealmToken", "wsse", "Realm", "", m_realm.get());
  244. req_header->addHeaderBlock(oneblock);
  245. }
  246. if (headers)
  247. {
  248. if (!req_header)
  249. req_header = new CHeader;
  250. ForEachItemIn(idx, *headers)
  251. {
  252. req_header->addHeaderBlock(&headers->item(idx));
  253. headers->remove(idx, true);
  254. }
  255. }
  256. // Form request
  257. Owned<CEnvelope> req_envelope;
  258. req_envelope.setown(new CEnvelope(req_header, req_body));
  259. Owned<CMimeMultiPart> multipart;
  260. multipart.setown(new CMimeMultiPart("1.0", "", "MIME_boundary", "text/xml", "soaproot"));
  261. req_envelope->marshall(multipart);
  262. StringBuffer requeststr;
  263. StringBuffer contenttypestr;
  264. if(multipart->getBodyCount() == 1)
  265. {
  266. CMimeBodyPart* rootpart = multipart->queryRootPart();
  267. rootpart->getContent(requeststr);
  268. }
  269. else
  270. {
  271. multipart->serialize(contenttypestr, requeststr);
  272. }
  273. Owned<CSoapRequest> soap_request = new CSoapRequest();
  274. soap_request->set_content_type(contenttypestr.str());
  275. soap_request->set_text(requeststr.str());
  276. #if defined(DEBUG_HTTP_)
  277. DBGLOG("SOAP Request content: %s", requeststr.str());
  278. #endif
  279. Owned<CSoapResponse> soap_response = new CSoapResponse();
  280. //Send request and get response
  281. if(m_transportclient.get() == NULL)
  282. {
  283. Owned<IHttpClientContext> httpctx = getHttpClientContext();
  284. IHttpClient* httpclient = httpctx->createHttpClient(call->getProxy(), call->get_url());
  285. m_transportclient.setown(httpclient);
  286. if (m_disableKeepAlive)
  287. httpclient->disableKeepAlive();
  288. if(m_username.length() > 0)
  289. {
  290. httpclient->setUserID(m_username.get());
  291. httpclient->setPassword(m_password.get());
  292. }
  293. if(m_realm.length() > 0)
  294. {
  295. httpclient->setRealm(m_realm.get());
  296. }
  297. }
  298. m_transportclient->postRequest(*soap_request.get(), *soap_response.get());
  299. int retstatus = soap_response->get_status();
  300. if(retstatus != SOAP_OK)
  301. {
  302. const char* errmsg = "SOAP error";
  303. if(retstatus == SOAP_CLIENT_ERROR)
  304. errmsg = "SOAP client error";
  305. else if(retstatus == SOAP_SERVER_ERROR)
  306. errmsg = "SOAP server error";
  307. else if(retstatus == SOAP_RPC_ERROR)
  308. errmsg = "SOAP rpc error";
  309. else if(retstatus == SOAP_CONNECTION_ERROR)
  310. errmsg = "SOAP Connection error";
  311. else if(retstatus == SOAP_REQUEST_TYPE_ERROR)
  312. errmsg = "SOAP request type error";
  313. else if(retstatus == SOAP_AUTHENTICATION_ERROR)
  314. errmsg = "SOAP authentication error";
  315. throw MakeStringException(retstatus, "%s", errmsg);
  316. }
  317. if(soap_response->get_text_length() == 0)
  318. {
  319. throw MakeStringException(-1, "Empty SOAP message received");
  320. }
  321. StringBuffer& resptext = soap_response->query_text();
  322. responsebuf.swapWith(resptext);
  323. return SOAP_OK;
  324. }