soapclient.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  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. }
  111. Owned<CSoapRequest> soap_request;
  112. soap_request.setown(new CSoapRequest);
  113. //Use the provided contenttype to overwrite the default content type.
  114. if(contenttype != NULL && strlen(contenttype) > 0)
  115. soap_request->set_content_type(contenttype);
  116. else
  117. soap_request->set_content_type(contenttypestr.str());
  118. soap_request->set_text(requeststr.str());
  119. if(soapaction != NULL && strlen(soapaction) > 0)
  120. soap_request->set_soapaction(soapaction);
  121. Owned<CSoapResponse> soap_response;
  122. soap_response.setown(new CSoapResponse);
  123. //Send request and get response
  124. if(m_transportclient.get() == NULL)
  125. {
  126. Owned<IHttpClientContext> httpctx = getHttpClientContext();
  127. IHttpClient* httpclient = httpctx->createHttpClient(call->getProxy(), call->get_url());
  128. m_transportclient.setown(httpclient);
  129. if (m_disableKeepAlive)
  130. httpclient->disableKeepAlive();
  131. if(m_username.length() > 0)
  132. {
  133. httpclient->setUserID(m_username.get());
  134. httpclient->setPassword(m_password.get());
  135. }
  136. if(m_realm.length() > 0)
  137. {
  138. httpclient->setRealm(m_realm.get());
  139. }
  140. }
  141. m_transportclient->postRequest(*soap_request.get(), *soap_response.get());
  142. int retstatus = soap_response->get_status();
  143. if(retstatus != SOAP_OK)
  144. {
  145. StringBuffer errmsg = "SOAP error";
  146. if(retstatus == SOAP_CLIENT_ERROR)
  147. errmsg = "SOAP client error";
  148. else if(retstatus == SOAP_SERVER_ERROR)
  149. errmsg = "SOAP server error";
  150. else if(retstatus == SOAP_RPC_ERROR)
  151. errmsg = "SOAP rpc error";
  152. else if(retstatus == SOAP_CONNECTION_ERROR)
  153. errmsg = "SOAP Connection error";
  154. else if(retstatus == SOAP_REQUEST_TYPE_ERROR)
  155. errmsg = "SOAP request type error";
  156. else if(retstatus == SOAP_AUTHENTICATION_ERROR)
  157. errmsg = "SOAP authentication error";
  158. if (soap_response->get_err())
  159. errmsg.appendf("[%s]", soap_response->get_err());
  160. throw MakeStringException(retstatus, "%s", errmsg.str());
  161. }
  162. #if defined(DEBUG_HTTP_)
  163. DBGLOG("response content type = %s", soap_response->get_content_type());
  164. #endif
  165. 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")))
  166. {
  167. responsebuf.append(soap_response->get_text());
  168. }
  169. else if(!Utils::strncasecmp(soap_response->get_content_type(), "Multipart/Related", strlen("Multipart/Related")))
  170. {
  171. CMimeMultiPart* parts = resp_multipart ? resp_multipart : new CMimeMultiPart("1.0", "Multipart/Related", "", "", "");
  172. parts->unserialize(soap_response->get_content_type(), soap_response->get_text_length(), soap_response->get_text());
  173. CMimeBodyPart* rootpart = parts->queryRootPart();
  174. rootpart->getContent(responsebuf);
  175. if (!resp_multipart)
  176. delete parts;
  177. }
  178. else
  179. {
  180. DBGLOG("SOAP Response type %s not supported", soap_response->get_content_type());
  181. return SOAP_REQUEST_TYPE_ERROR;
  182. }
  183. if(responsebuf.length() == 0)
  184. {
  185. throw MakeStringException(-1, "Empty SOAP message received");
  186. }
  187. return SOAP_OK;
  188. }
  189. int CSoapClient::postRequest(const char* contenttype, const char* soapaction, IRpcMessage& rpccall, IRpcMessage& rpcresponse)
  190. {
  191. StringBuffer responsebuf;
  192. Owned<CMimeMultiPart> parts = new CMimeMultiPart("1.0", "Multipart/Related", "", "", "");
  193. int rt = postRequest(contenttype, soapaction, rpccall, responsebuf, parts);
  194. if (rt != SOAP_OK)
  195. return rt;
  196. // DBGLOG("response SoapClient got from soap server = \n%s", responsebuf.str());
  197. auto_ptr<XmlPullParser> xpp(new XmlPullParser());
  198. int bufSize = responsebuf.length();
  199. xpp->setSupportNamespaces(true);
  200. xpp->setInput(responsebuf.str(), bufSize);
  201. int type;
  202. StartTag stag;
  203. EndTag etag;
  204. Owned<CEnvelope> res_envelope;
  205. res_envelope.setown(new CEnvelope);
  206. while((type = xpp->next()) != XmlPullParser::END_DOCUMENT)
  207. {
  208. if(type == XmlPullParser::START_TAG) {
  209. xpp->readStartTag(stag);
  210. if(!stricmp(stag.getLocalName(), SOAP_ENVELOPE_NAME))
  211. {
  212. res_envelope->unmarshall(xpp.get());
  213. break;
  214. }
  215. }
  216. }
  217. CBody* res_body = res_envelope->get_body();
  218. CRpcResponse* response = (CRpcResponse *)&rpcresponse;
  219. res_body->nextRpcMessage(response);
  220. response->unmarshall(xpp.get(), parts.get());
  221. return SOAP_OK;
  222. }
  223. int CSoapClient::postRequest(IRpcMessage & rpccall, StringBuffer & responsebuf)
  224. {
  225. return postRequest(rpccall, responsebuf, NULL);
  226. }
  227. int CSoapClient::postRequest(IRpcMessage & rpccall, StringBuffer & responsebuf, IRpcMessageArray *headers)
  228. {
  229. CRpcCall* call = (CRpcCall *)&rpccall;
  230. CBody* req_body = new CBody;
  231. req_body->add_rpcmessage(call);
  232. CHeader* req_header = NULL;
  233. if(m_username.length() > 0)
  234. {
  235. req_header = new CHeader;
  236. IRpcMessage* oneblock = new CRpcMessage("Security");
  237. oneblock->set_ns("wsse");
  238. oneblock->add_value("", "wsse", "UsernameToken", "", "");
  239. oneblock->add_value("UsernameToken", "wsse", "Username", "", m_username.get());
  240. oneblock->add_value("UsernameToken", "wsse", "Password", "", m_password.get());
  241. oneblock->add_value("RealmToken", "wsse", "Realm", "", m_realm.get());
  242. req_header->addHeaderBlock(oneblock);
  243. }
  244. if (headers)
  245. {
  246. if (!req_header)
  247. req_header = new CHeader;
  248. ForEachItemIn(idx, *headers)
  249. {
  250. req_header->addHeaderBlock(&headers->item(idx));
  251. headers->remove(idx, true);
  252. }
  253. }
  254. // Form request
  255. Owned<CEnvelope> req_envelope;
  256. req_envelope.setown(new CEnvelope(req_header, req_body));
  257. Owned<CMimeMultiPart> multipart;
  258. multipart.setown(new CMimeMultiPart("1.0", "", "MIME_boundary", "text/xml", "soaproot"));
  259. req_envelope->marshall(multipart);
  260. StringBuffer requeststr;
  261. StringBuffer contenttypestr;
  262. if(multipart->getBodyCount() == 1)
  263. {
  264. CMimeBodyPart* rootpart = multipart->queryRootPart();
  265. rootpart->getContent(requeststr);
  266. }
  267. else
  268. {
  269. multipart->serialize(contenttypestr, requeststr);
  270. }
  271. Owned<CSoapRequest> soap_request = new CSoapRequest();
  272. soap_request->set_content_type(contenttypestr.str());
  273. soap_request->set_text(requeststr.str());
  274. #if defined(DEBUG_HTTP_)
  275. DBGLOG("SOAP Request content: %s", requeststr.str());
  276. #endif
  277. Owned<CSoapResponse> soap_response = new CSoapResponse();
  278. //Send request and get response
  279. if(m_transportclient.get() == NULL)
  280. {
  281. Owned<IHttpClientContext> httpctx = getHttpClientContext();
  282. IHttpClient* httpclient = httpctx->createHttpClient(call->getProxy(), call->get_url());
  283. m_transportclient.setown(httpclient);
  284. if (m_disableKeepAlive)
  285. httpclient->disableKeepAlive();
  286. if(m_username.length() > 0)
  287. {
  288. httpclient->setUserID(m_username.get());
  289. httpclient->setPassword(m_password.get());
  290. }
  291. if(m_realm.length() > 0)
  292. {
  293. httpclient->setRealm(m_realm.get());
  294. }
  295. }
  296. m_transportclient->postRequest(*soap_request.get(), *soap_response.get());
  297. int retstatus = soap_response->get_status();
  298. if(retstatus != SOAP_OK)
  299. {
  300. const char* errmsg = "SOAP error";
  301. if(retstatus == SOAP_CLIENT_ERROR)
  302. errmsg = "SOAP client error";
  303. else if(retstatus == SOAP_SERVER_ERROR)
  304. errmsg = "SOAP server error";
  305. else if(retstatus == SOAP_RPC_ERROR)
  306. errmsg = "SOAP rpc error";
  307. else if(retstatus == SOAP_CONNECTION_ERROR)
  308. errmsg = "SOAP Connection error";
  309. else if(retstatus == SOAP_REQUEST_TYPE_ERROR)
  310. errmsg = "SOAP request type error";
  311. else if(retstatus == SOAP_AUTHENTICATION_ERROR)
  312. errmsg = "SOAP authentication error";
  313. throw MakeStringException(retstatus, "%s", errmsg);
  314. }
  315. if(soap_response->get_text_length() == 0)
  316. {
  317. throw MakeStringException(-1, "Empty SOAP message received");
  318. }
  319. StringBuffer& resptext = soap_response->query_text();
  320. responsebuf.swapWith(resptext);
  321. return SOAP_OK;
  322. }