diff options
author | Stefan Radomski <github@mintwerk.de> | 2017-06-13 10:19:24 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-06-13 10:19:24 (GMT) |
commit | b3a2d91805feb81f79ee52c30a077521912b0bf9 (patch) | |
tree | 8b57e9244576eaa1c721df44899009f3b8d10f05 /src/uscxml/plugins/element | |
parent | 4b861a6af4eec8a58d3515e871ccdadd44a182fd (diff) | |
parent | a43c42980727e0376c6bfa44576a54e6d3c26687 (diff) | |
download | uscxml-b3a2d91805feb81f79ee52c30a077521912b0bf9.zip uscxml-b3a2d91805feb81f79ee52c30a077521912b0bf9.tar.gz uscxml-b3a2d91805feb81f79ee52c30a077521912b0bf9.tar.bz2 |
Merge pull request #146 from tklab-tud/sradomski
respond element and proper http ioproc
Diffstat (limited to 'src/uscxml/plugins/element')
-rw-r--r-- | src/uscxml/plugins/element/CMakeLists.txt | 26 | ||||
-rw-r--r-- | src/uscxml/plugins/element/respond/RespondElement.cpp | 179 | ||||
-rw-r--r-- | src/uscxml/plugins/element/respond/RespondElement.h | 60 |
3 files changed, 265 insertions, 0 deletions
diff --git a/src/uscxml/plugins/element/CMakeLists.txt b/src/uscxml/plugins/element/CMakeLists.txt new file mode 100644 index 0000000..a32bdff --- /dev/null +++ b/src/uscxml/plugins/element/CMakeLists.txt @@ -0,0 +1,26 @@ +# Respond element + +OPTION(WITH_ELEMENT_RESPOND "Build the respond element" ON) +if (WITH_ELEMENT_RESPOND) + set(USCXML_ELEMENTS "respond ${USCXML_ELEMENTS}") + file(GLOB_RECURSE ELEMENT_RESPOND + respond/*.cpp + respond/*.h) + if (BUILD_AS_PLUGINS) + source_group("" FILES ${ELEMENT_RESPOND}) + add_library(element_respond SHARED ${ELEMENT_RESPOND} "../Plugins.cpp") + target_link_libraries(element_respond + uscxml + ) + set_target_properties(element_respond PROPERTIES FOLDER "Plugins//Elements") + set_target_properties(element_respond PROPERTIES COMPILE_FLAGS "-DPLUMA_EXPORTS") + set_target_properties(element_respond PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/plugins") + else() + list (APPEND USCXML_FILES ${ELEMENT_RESPOND}) + endif() +endif() + + +set(USCXML_INCLUDE_DIRS ${USCXML_INCLUDE_DIRS} PARENT_SCOPE) +set(USCXML_FILES ${USCXML_FILES} PARENT_SCOPE) +set(USCXML_ELEMENTS ${USCXML_ELEMENTS} PARENT_SCOPE) diff --git a/src/uscxml/plugins/element/respond/RespondElement.cpp b/src/uscxml/plugins/element/respond/RespondElement.cpp new file mode 100644 index 0000000..14e3fbd --- /dev/null +++ b/src/uscxml/plugins/element/respond/RespondElement.cpp @@ -0,0 +1,179 @@ +/** + * @file + * @author 2017 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#include "RespondElement.h" +#include "uscxml/util/DOM.h" +#include "uscxml/server/HTTPServer.h" +#include "uscxml/interpreter/LoggingImpl.h" +#include "uscxml/plugins/ioprocessor/http/HTTPIOProcessor.h" + +#ifdef BUILD_AS_PLUGINS +#include <Pluma/Connector.hpp> +#endif + +namespace uscxml { + +using namespace XERCESC_NS; + +void RespondElement::enterElement(XERCESC_NS::DOMElement* node) { + // try to get the request id + if (!HAS_ATTR(node, X("to"))) { + ERROR_EXECUTION_THROW2("Respond element requires 'to' attribute", node); + } + if (HAS_ATTR(node, X("to")) && !_interpreter->getActionLanguage()->dataModel) { + ERROR_EXECUTION_THROW2("Respond element with 'to' requires datamodel", node); + } + std::string requestId = _interpreter->getActionLanguage()->dataModel.evalAsData(ATTR(node, X("to"))).atom; + + auto ioProcs = _interpreter->getIOProcessors(); + if (ioProcs.find("http") == ioProcs.end()) { + ERROR_EXECUTION_THROW2("No HTTP ioprocessor associated with interpreter", node); + } + + // try to get the request object + IOProcessor ioProc = ioProcs["http"]; + HTTPIOProcessor* http = (HTTPIOProcessor*)(ioProc.getImpl().operator->()); + + + if (http->getUnansweredRequests().find(requestId) == http->getUnansweredRequests().end()) { + ERROR_EXECUTION_THROW2("No unanswered HTTP request with given id", node); + } + + HTTPServer::Request httpReq = http->getUnansweredRequests()[requestId].second; + + assert(httpReq.evhttpReq != NULL); + HTTPServer::Reply httpReply(httpReq); + + // get the status or default to 200 + std::string statusStr = (HAS_ATTR(node, X("status")) ? ATTR(node, X("status")) : "200"); + if (!isNumeric(statusStr.c_str(), 10)) { + ERROR_EXECUTION_THROW2("Respond element with non-numeric status: " + statusStr, node); + } + httpReply.status = strTo<int>(statusStr);; + + // extract the content + std::list<DOMElement*> contents = DOMUtils::filterChildElements(XML_PREFIX(node).str() + "content", node); + if (contents.size() > 0) { + DOMElement* contentElem = contents.front(); + if (HAS_ATTR(contentElem, kXMLCharExpr)) { + // -- content is evaluated string from datamodel + if (_interpreter->getActionLanguage()->dataModel) { + try { + Data contentData = _interpreter->getActionLanguage()->dataModel.evalAsData(ATTR(contentElem, kXMLCharExpr)); + if (contentData.atom.length() > 0) { + httpReply.content = contentData.atom; + httpReply.headers["Content-Type"] = "text/plain"; + } else if (contentData.binary) { + httpReply.content = std::string(contentData.binary.getData(), contentData.binary.getSize()); + httpReply.headers["Content-Type"] = contentData.binary.getMimeType(); + } else if (contentData.node) { + std::stringstream ss; + ss << contentData.node; + httpReply.content = ss.str();; + httpReply.headers["Content-Type"] = "application/xml"; + } else { + httpReply.content = Data::toJSON(contentData); + httpReply.headers["Content-Type"] = "application/json"; + } + } catch (Event e) { + ERROR_EXECUTION_RETHROW(e, "Syntax error with expr in content child of respond element", node); + } + } else { + ERROR_EXECUTION_THROW2("content element has expr attribute but no datamodel is specified", node); + } + } else if (HAS_ATTR(contentElem, X("file")) || HAS_ATTR(contents.front(), X("fileexpr"))) { + // -- content is from file + URL file; + if (HAS_ATTR(contentElem, X("fileexpr"))) { + if (_interpreter->getActionLanguage()->dataModel) { + try { + file = _interpreter->getActionLanguage()->dataModel.evalAsData(ATTR(contentElem, X("fileexpr"))).atom; + } catch (Event e) { + ERROR_EXECUTION_RETHROW(e, "Syntax error with fileexpr in content child of respond element", node); + } + } + } else { + file = ATTR(contentElem, X("file")); + } + if (file) { + httpReply.content = file.getInContent(); + size_t lastDot; + if ((lastDot = file.path().find_last_of(".")) != std::string::npos) { + std::string extension = file.path().substr(lastDot + 1); + std::string mimeType = URL::getMimeType(extension); + if (mimeType.length() > 0) { + httpReply.headers["Content-Type"] = mimeType; + } + } + } + } else if (contentElem->hasChildNodes()) { // -- content embedded as child nodes ------ + httpReply.content = X(contentElem->getFirstChild()->getNodeValue()).str(); + } else { + ERROR_EXECUTION_THROW2("content element does not specify any content", node); + } + } + + // process headers + std::list<DOMElement*> headers = DOMUtils::filterChildElements(XML_PREFIX(node).str() + "header", node); + for (auto headerElem : headers) { + + std::string name; + if (HAS_ATTR(headerElem, kXMLCharName)) { + name = ATTR(headerElem, kXMLCharName); + } else if(HAS_ATTR(headerElem, X("nameexpr"))) { + if (_interpreter->getActionLanguage()->dataModel) { + try { + name = _interpreter->getActionLanguage()->dataModel.evalAsData(ATTR(headerElem, X("nameexpr"))).atom; + } catch (Event e) { + ERROR_EXECUTION_RETHROW(e, "Syntax error with nameexpr in header child of Respond element", headerElem); + } + } else { + ERROR_EXECUTION_THROW2("header element has nameexpr attribute but no datamodel is specified", headerElem); + } + } else { + ERROR_EXECUTION_THROW2("header element has no name or nameexpr attribute", headerElem); + } + + std::string value; + if (HAS_ATTR(headerElem, X("value"))) { + value = ATTR(headerElem, X("value")); + } else if(HAS_ATTR(headerElem, X("valueexpr"))) { + if (_interpreter->getActionLanguage()->dataModel) { + try { + value = _interpreter->getActionLanguage()->dataModel.evalAsData(ATTR(headerElem, X("valueexpr"))).atom; + } catch (Event e) { + ERROR_EXECUTION_RETHROW(e, "Syntax error with valueexpr in header child of Respond element", headerElem); + } + } else { + ERROR_EXECUTION_THROW2("header element has valueexpr attribute but no datamodel is specified", headerElem); + } + } else { + ERROR_EXECUTION_THROW2("header element has no value or valueexpr attribute", headerElem); + return; + } + + httpReply.headers[name] = value; + } + + // send the reply + HTTPServer::reply(httpReply); + http->getUnansweredRequests().erase(requestId); +} + +} diff --git a/src/uscxml/plugins/element/respond/RespondElement.h b/src/uscxml/plugins/element/respond/RespondElement.h new file mode 100644 index 0000000..09c9147 --- /dev/null +++ b/src/uscxml/plugins/element/respond/RespondElement.h @@ -0,0 +1,60 @@ +/** + * @file + * @author 2017 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see <http://www.opensource.org/licenses/bsd-license>. + * @endcond + */ + +#ifndef RESPONDELEMENT_H_564843 +#define RESPONDELEMENT_H_564843 + +#include "uscxml/config.h" +#include "uscxml/interpreter/InterpreterImpl.h" +#include "uscxml/interpreter/BasicEventQueue.h" +#include "uscxml/interpreter/LoggingImpl.h" + +#include "uscxml/plugins/ExecutableContentImpl.h" + +#ifdef BUILD_AS_PLUGINS +#include "uscxml/plugins/Plugins.h" +#endif + +namespace uscxml { + +/** +* @ingroup element + * The respond element to reply to proper HTTP requests + */ +class USCXML_API RespondElement : public ExecutableContentImpl { +public: + RespondElement() {}; + virtual ~RespondElement() {}; + + virtual std::shared_ptr<ExecutableContentImpl> create(InterpreterImpl* interpreter) { + std::shared_ptr<RespondElement> element(new RespondElement()); + element->_interpreter = interpreter; + return element; + } + + virtual std::string getLocalName() { + return "respond"; + } + virtual void enterElement(XERCESC_NS::DOMElement* node); + virtual void exitElement(XERCESC_NS::DOMElement* node) {} + +}; + +} +#endif /* end of include guard: RESPONDELEMENT_H_564843 */ |