summaryrefslogtreecommitdiffstats
path: root/src/uscxml/plugins/element
diff options
context:
space:
mode:
Diffstat (limited to 'src/uscxml/plugins/element')
-rw-r--r--src/uscxml/plugins/element/CMakeLists.txt26
-rw-r--r--src/uscxml/plugins/element/respond/RespondElement.cpp179
-rw-r--r--src/uscxml/plugins/element/respond/RespondElement.h60
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 */