soapbind.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  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. //Jlib
  21. #include "jliball.hpp"
  22. //SCM Interfaces
  23. #include "esp.hpp"
  24. //ESP Core
  25. #include "espthread.hpp"
  26. //ESP Bindings
  27. #include "SOAP/Platform/soapbind.hpp"
  28. #include "SOAP/client/soapclient.hpp"
  29. #include "http/platform/httpprot.hpp"
  30. #include "http/platform/httpservice.hpp"
  31. #include "SOAP/Platform/soapservice.hpp"
  32. #ifdef WIN32
  33. #define ESP_FACTORY __declspec(dllexport)
  34. #else
  35. #define ESP_FACTORY
  36. #endif
  37. CSoapBinding::CSoapBinding()
  38. {
  39. }
  40. CSoapBinding::~CSoapBinding()
  41. {
  42. }
  43. //IEspRpcBinding
  44. const char * CSoapBinding::getRpcType()
  45. {
  46. return "soap";
  47. }
  48. const char * CSoapBinding::getTransportType()
  49. {
  50. return "unknown";
  51. }
  52. int CSoapBinding::processRequest(IRpcMessage* rpc_call, IRpcMessage* rpc_response)
  53. {
  54. return 0;
  55. }
  56. CHttpSoapBinding::CHttpSoapBinding():EspHttpBinding(NULL, NULL, NULL)
  57. {
  58. log_level_=hsl_none;
  59. }
  60. CHttpSoapBinding::CHttpSoapBinding(IPropertyTree* cfg, const char *bindname, const char *procname, http_soap_log_level level)
  61. : EspHttpBinding(cfg, bindname, procname)
  62. {
  63. log_level_=level;
  64. }
  65. CHttpSoapBinding::~CHttpSoapBinding()
  66. {
  67. }
  68. const char * CHttpSoapBinding::getTransportType()
  69. {
  70. return "http";
  71. }
  72. static CSoapFault* makeSoapFault(CHttpRequest* request, IMultiException* me, const char *ns)
  73. {
  74. const char* svcName = request->queryServiceName();
  75. if (svcName && *svcName)
  76. {
  77. const char* method = request->queryServiceMethod();
  78. StringBuffer host;
  79. const char* wsdlAddr = request->queryParameters()->queryProp("__wsdl_address");
  80. if (wsdlAddr && *wsdlAddr)
  81. host.append(wsdlAddr);
  82. else
  83. {
  84. host.append(request->queryHost());
  85. if (request->getPort()>0)
  86. host.append(":").append(request->getPort());
  87. }
  88. VStringBuffer ns_ext("xmlns=\"%s\""
  89. " xsi:schemaLocation=\"%s %s/%s/%s?xsd\"",
  90. ns, ns, host.str(), svcName, method ? method : "");
  91. return new CSoapFault(me, ns_ext);
  92. }
  93. return new CSoapFault(me);
  94. }
  95. int CHttpSoapBinding::onSoapRequest(CHttpRequest* request, CHttpResponse* response)
  96. {
  97. Owned<CSoapFault> soapFault;
  98. try
  99. {
  100. return HandleSoapRequest(request,response);
  101. }
  102. catch (IMultiException* mex)
  103. {
  104. StringBuffer ns;
  105. soapFault.setown(makeSoapFault(request,mex, generateNamespace(*request->queryContext(), request, request->queryServiceName(), request->queryServiceMethod(), ns).str()));
  106. //SetHTTPErrorStatus(mex->errorCode(),response);
  107. SetHTTPErrorStatus(500,response);
  108. mex->Release();
  109. }
  110. catch (IException* e)
  111. {
  112. StringBuffer ns;
  113. Owned<IMultiException> mex = MakeMultiException("Esp");
  114. mex->append(*e); // e is owned by mex
  115. soapFault.setown(makeSoapFault(request,mex, generateNamespace(*request->queryContext(), request, request->queryServiceName(), request->queryServiceMethod(), ns).str()));
  116. SetHTTPErrorStatus(500,response);
  117. }
  118. catch (...)
  119. {
  120. soapFault.setown(new CSoapFault(500,"Internal Server Error"));
  121. SetHTTPErrorStatus(500,response);
  122. }
  123. //response->setContentType(soapFault->get_content_type());
  124. response->setContentType(HTTP_TYPE_TEXT_XML_UTF8);
  125. response->setContent(soapFault->get_text());
  126. DBGLOG("Sending SOAP Fault(%u): %s", response->getContentLength(), response->queryContent());
  127. {
  128. EspTimeSection sendtime("send fault [CHttpSoapBinding::onSoapRequest]");
  129. response->send();
  130. }
  131. return -1;
  132. }
  133. int CHttpSoapBinding::HandleSoapRequest(CHttpRequest* request, CHttpResponse* response)
  134. {
  135. ESP_TIME_SECTION("CHttpSoapBinding::onSoapRequest");
  136. StringBuffer requeststr;
  137. request->getContent(requeststr);
  138. if(requeststr.length() == 0)
  139. throw MakeStringException(-1, "Content read is empty");
  140. Owned<CSoapService> soapservice;
  141. Owned<CSoapRequest> soaprequest;
  142. Owned<CSoapResponse> soapresponse;
  143. //soapservice.setown(new CSoapService((IEspSoapBinding*)(this)));
  144. soapservice.setown(new CSoapService(this));
  145. soaprequest.setown(new CSoapRequest);
  146. soaprequest->set_text(requeststr.str());
  147. StringBuffer contenttype;
  148. request->getContentType(contenttype);
  149. soaprequest->set_content_type(contenttype.str());
  150. soaprequest->setContext(request->queryContext());
  151. CMimeMultiPart* multipart = request->queryMultiPart();
  152. if(multipart != NULL)
  153. {
  154. soaprequest->setOwnMultiPart(LINK(multipart));
  155. }
  156. soapresponse.setown(new CSoapResponse);
  157. StringBuffer reqPath;
  158. request->getPath(reqPath);
  159. setRequestPath(reqPath.str());
  160. soapservice->processRequest(*soaprequest.get(), *soapresponse.get());
  161. response->setVersion(HTTP_VERSION);
  162. int status = soapresponse->get_status();
  163. if(status == SOAP_OK)
  164. response->setStatus(HTTP_STATUS_OK);
  165. else if(status == SOAP_SERVER_ERROR || status == SOAP_RPC_ERROR || status == SOAP_CONNECTION_ERROR)
  166. {
  167. StringBuffer msg("Internal Server Error");
  168. const char* detail = soapresponse->get_err();
  169. if (detail && *detail)
  170. msg.appendf(" [%s]", detail);
  171. throw MakeStringException(500, "%s", msg.str());
  172. }
  173. else if(status == SOAP_CLIENT_ERROR || status == SOAP_REQUEST_TYPE_ERROR)
  174. {
  175. StringBuffer msg("Bad Request");
  176. const char* detail = soapresponse->get_err();
  177. if (detail && *detail)
  178. msg.appendf(" [%s]", detail);
  179. throw MakeStringException(400, "%s", msg.str());
  180. }
  181. else if(status == SOAP_AUTHENTICATION_REQUIRED)
  182. response->sendBasicChallenge(m_challenge_realm.str(), false);
  183. else if(status == SOAP_AUTHENTICATION_ERROR)
  184. {
  185. throw MakeStringException(401,"Unauthorized Access");
  186. }
  187. else
  188. response->setStatus(HTTP_STATUS_OK);
  189. response->setContentType(soapresponse->get_content_type());
  190. response->setContent(soapresponse->get_text());
  191. DBGLOG("Sending SOAP Response(%u)", response->getContentLength());
  192. {
  193. EspTimeSection sendtime("send response [CHttpSoapBinding::HandleSoapRequest]");
  194. response->send();
  195. }
  196. return 0;
  197. }
  198. void CSoapRequestBinding::post(const char *proxy, const char* url, IRpcResponseBinding& response, const char *soapaction)
  199. {
  200. CRpcCall rpccall;
  201. CRpcResponse rpcresponse;
  202. rpccall.set_url(url);
  203. rpccall.setProxy(proxy);
  204. serialize(*static_cast<IRpcMessage*>(&rpccall));
  205. CSoapClient soapclient; //to add support for handling cookies soapclient(false);
  206. soapclient.setUsernameToken(getUserId(), getPassword(), getRealm());
  207. int result = soapclient.postRequest(soapaction, rpccall, rpcresponse);
  208. if(result == SOAP_OK)
  209. {
  210. response.setRpcState(RPC_MESSAGE_OK);
  211. response.unserialize(rpcresponse, NULL, NULL);
  212. }
  213. else if(result == SOAP_CONNECTION_ERROR)
  214. {
  215. response.setRpcState(RPC_MESSAGE_CONNECTION_ERROR);
  216. }
  217. else
  218. {
  219. response.setRpcState(RPC_MESSAGE_ERROR);
  220. }
  221. }
  222. void CSoapComplexType::appendContent(IEspContext* ctx, MemoryBuffer& buffer, StringBuffer& mimetype)
  223. {
  224. StringBuffer content;
  225. if (ctx && ctx->getResponseFormat()==ESPSerializationJSON)
  226. {
  227. content.append('{');
  228. serializeStruct(ctx, content, (const char *)NULL);
  229. content.append('}');
  230. mimetype.set("application/json; charset=UTF-8");
  231. }
  232. else
  233. {
  234. buffer.append(38, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
  235. serializeStruct(ctx, content, (const char *)NULL);
  236. mimetype.set("text/xml; charset=UTF-8");
  237. }
  238. buffer.append(content.length(), content.str());
  239. }
  240. inline void open_element(IEspContext *ctx, StringBuffer &xml, const char *name, const char *uri, const char *prefix)
  241. {
  242. if (!name || !*name)
  243. return;
  244. xml.append("<");
  245. if (prefix && *prefix)
  246. xml.append(prefix).append(':');
  247. xml.append(name);
  248. if (uri && *uri)
  249. {
  250. xml.append("xmlns");
  251. if (prefix && *prefix)
  252. xml.append(':').append(prefix);
  253. xml.append("=\"").append(uri).append('\"');
  254. }
  255. }
  256. inline void start_child_attributes(IEspContext *ctx, StringBuffer &xml, const char *name)
  257. {
  258. }
  259. inline void start_child_elements(IEspContext *ctx, StringBuffer &xml, const char *name)
  260. {
  261. if (!name || !*name)
  262. return;
  263. xml.append('>');
  264. }
  265. inline void close_element(IEspContext *ctx, StringBuffer &xml, const char *name, const char *prefix)
  266. {
  267. if (!name || !*name)
  268. return;
  269. xml.append("</");
  270. if (prefix && *prefix)
  271. xml.append(prefix).append(':');
  272. xml.append(name).append('>');
  273. }
  274. void CSoapComplexType::serializeJSONStruct(IEspContext* ctx, StringBuffer& s, const char *name)
  275. {
  276. if (ctx && ctx->getResponseFormat()==ESPSerializationJSON)
  277. {
  278. if (s.length() && !strchr("[{:", s.charAt(s.length()-1)))
  279. s.append(", ");
  280. if (name && *name)
  281. s.append('\"').append(name).append("\": ");
  282. s.append("{");
  283. serializeContent(ctx, s);
  284. s.append("}");
  285. return;
  286. }
  287. }
  288. void CSoapComplexType::serializeStruct(IEspContext* ctx, StringBuffer& s, const char *name)
  289. {
  290. const char *tag = (name && *name) ? name : getRootName();
  291. if (ctx && ctx->getResponseFormat()==ESPSerializationJSON)
  292. return serializeJSONStruct(ctx, s, tag);
  293. open_element(ctx, s, tag, getNsURI(), getNsPrefix());
  294. start_child_attributes(ctx, s, name);
  295. serializeAttributes(ctx, s);
  296. start_child_elements(ctx, s, tag);
  297. serializeContent(ctx, s);
  298. close_element(ctx, s, tag, getNsPrefix());
  299. }
  300. void CSoapComplexType::serializeItem(IEspContext* ctx, StringBuffer& s, const char *name)
  301. {
  302. if (ctx && ctx->getResponseFormat()==ESPSerializationJSON)
  303. return serializeJSONStruct(ctx, s, NULL);
  304. serializeStruct(ctx, s, name);
  305. }
  306. void CSoapResponseBinding::handleExceptions(IMultiException *me, const char *serv, const char *meth)
  307. {
  308. if (me->ordinality() > 0)
  309. {
  310. StringBuffer text;
  311. me->errorMessage(text);
  312. text.append('\n');
  313. WARNLOG("Exception(s) in %s::%s - %s", serv, meth, text.str());
  314. IArrayOf<IException>& exceptions = me->getArray();
  315. ForEachItemIn(i, exceptions)
  316. noteException(*LINK(&exceptions.item(i)));
  317. }
  318. }
  319. void SetHTTPErrorStatus(int ErrorCode,CHttpResponse* response)
  320. {
  321. switch(ErrorCode)
  322. {
  323. case 204:
  324. response->setStatus(HTTP_STATUS_NO_CONTENT);
  325. break;
  326. case 301:
  327. response->setStatus(HTTP_STATUS_MOVED_PERMANENTLY);
  328. break;
  329. case 302:
  330. response->setStatus(HTTP_STATUS_REDIRECT);
  331. break;
  332. case 303:
  333. response->setStatus(HTTP_STATUS_REDIRECT_POST);
  334. break;
  335. case 400:
  336. response->setStatus(HTTP_STATUS_BAD_REQUEST);
  337. break;
  338. case 401:
  339. response->setStatus(HTTP_STATUS_UNAUTHORIZED);
  340. break;
  341. case 403:
  342. response->setStatus(HTTP_STATUS_FORBIDDEN);
  343. break;
  344. case 404:
  345. response->setStatus(HTTP_STATUS_NOT_FOUND);
  346. break;
  347. case 405:
  348. response->setStatus(HTTP_STATUS_NOT_ALLOWED);
  349. break;
  350. case 501:
  351. response->setStatus(HTTP_STATUS_NOT_IMPLEMENTED);
  352. break;
  353. default:
  354. response->setStatus(HTTP_STATUS_INTERNAL_SERVER_ERROR);
  355. }
  356. }