summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Radomski <github@mintwerk.de>2017-06-12 14:37:12 (GMT)
committerStefan Radomski <github@mintwerk.de>2017-06-12 14:37:12 (GMT)
commitbfdd2f8e72005d1e2ef2a87d19d8ac242bb22557 (patch)
tree5c3e73450534bd8b4279d20f0171dcfb45167786
parent090016a3f5bcd66d6265fe1504430430f1dee7ed (diff)
downloaduscxml-bfdd2f8e72005d1e2ef2a87d19d8ac242bb22557.zip
uscxml-bfdd2f8e72005d1e2ef2a87d19d8ac242bb22557.tar.gz
uscxml-bfdd2f8e72005d1e2ef2a87d19d8ac242bb22557.tar.bz2
respond element and proper http ioproc
-rw-r--r--apps/uscxml-browser.cpp42
-rw-r--r--config.h.in4
-rw-r--r--src/uscxml/CMakeLists.txt3
-rw-r--r--src/uscxml/plugins/ExecutableContentImpl.h4
-rw-r--r--src/uscxml/plugins/Factory.cpp24
-rw-r--r--src/uscxml/plugins/IOProcessor.h4
-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
-rw-r--r--src/uscxml/plugins/ioprocessor/CMakeLists.txt25
-rw-r--r--src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp230
-rw-r--r--src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.h23
-rw-r--r--src/uscxml/plugins/ioprocessor/http/HTTPIOProcessor.cpp329
-rw-r--r--src/uscxml/plugins/ioprocessor/http/HTTPIOProcessor.h115
-rw-r--r--src/uscxml/server/HTTPServer.cpp28
-rw-r--r--src/uscxml/server/HTTPServer.h2
-rw-r--r--src/uscxml/util/Predicates.cpp2
-rw-r--r--src/uscxml/util/URL.cpp28
-rw-r--r--src/uscxml/util/URL.h3
-rw-r--r--test/w3c/manual/test-http.scxml14
20 files changed, 857 insertions, 288 deletions
diff --git a/apps/uscxml-browser.cpp b/apps/uscxml-browser.cpp
index 6a16f98..dd0633b 100644
--- a/apps/uscxml-browser.cpp
+++ b/apps/uscxml-browser.cpp
@@ -25,23 +25,25 @@ int main(int argc, char** argv) {
InterpreterOptions::printUsageAndExit(argv[0]);
}
- // setup HTTP server
- HTTPServer::SSLConfig* sslConf = NULL;
- if (options.certificate.length() > 0) {
- sslConf = new HTTPServer::SSLConfig();
- sslConf->privateKey = options.certificate;
- sslConf->publicKey = options.certificate;
- sslConf->port = options.httpsPort;
-
- } else if (options.privateKey.length() > 0 && options.publicKey.length() > 0) {
- sslConf = new HTTPServer::SSLConfig();
- sslConf->privateKey = options.privateKey;
- sslConf->publicKey = options.publicKey;
- sslConf->port = options.httpsPort;
-
- }
- HTTPServer::getInstance(options.httpPort, options.wsPort, sslConf);
+ if (!options.validate) {
+ // setup HTTP server
+ HTTPServer::SSLConfig* sslConf = NULL;
+ if (options.certificate.length() > 0) {
+ sslConf = new HTTPServer::SSLConfig();
+ sslConf->privateKey = options.certificate;
+ sslConf->publicKey = options.certificate;
+ sslConf->port = options.httpsPort;
+
+ } else if (options.privateKey.length() > 0 && options.publicKey.length() > 0) {
+ sslConf = new HTTPServer::SSLConfig();
+ sslConf->privateKey = options.privateKey;
+ sslConf->publicKey = options.publicKey;
+ sslConf->port = options.httpsPort;
+ }
+ HTTPServer::getInstance(options.httpPort, options.wsPort, sslConf);
+ }
+
if (options.pluginPath.length() > 0) {
Factory::setDefaultPluginPath(options.pluginPath);
}
@@ -55,7 +57,7 @@ int main(int argc, char** argv) {
for(size_t i = 0; i < options.interpreters.size(); i++) {
// InterpreterOptions* currOptions = options.interpreters[0].second;
- std::string documentURL = options.interpreters[0].first;
+ std::string documentURL = options.interpreters[i].first;
LOGD(USCXML_INFO) << "Processing " << documentURL << std::endl;
@@ -71,7 +73,7 @@ int main(int argc, char** argv) {
if (issues.size() == 0) {
LOGD(USCXML_DEBUG) << "No issues found" << std::endl;
}
-
+
}
if (options.verbose) {
@@ -90,6 +92,10 @@ int main(int argc, char** argv) {
}
}
+ if (options.validate) {
+ return EXIT_SUCCESS;
+ }
+
if (options.withDebugger) {
DebuggerServlet* debugger;
debugger = new DebuggerServlet();
diff --git a/config.h.in b/config.h.in
index ace7471..3955b87 100644
--- a/config.h.in
+++ b/config.h.in
@@ -65,8 +65,12 @@
#cmakedefine WITH_INV_DIRMON
#cmakedefine WITH_IOPROC_BASICHTTP
+#cmakedefine WITH_IOPROC_HTTP
+#cmakedefine WITH_IOPROC_HTTP_TIMEOUT @WITH_IOPROC_HTTP_TIMEOUT@
#cmakedefine WITH_IOPROC_SCXML
+#cmakedefine WITH_ELEMENT_RESPOND
+
#cmakedefine WITH_DM_ECMA_V8
#cmakedefine WITH_DM_ECMA_JSC
#cmakedefine WITH_DM_LUA
diff --git a/src/uscxml/CMakeLists.txt b/src/uscxml/CMakeLists.txt
index 276bd23..b22f6c7 100644
--- a/src/uscxml/CMakeLists.txt
+++ b/src/uscxml/CMakeLists.txt
@@ -88,10 +88,11 @@ if (BUILD_AS_PLUGINS)
endif()
add_subdirectory(plugins/datamodel)
-# add_subdirectory(plugins/element)
+add_subdirectory(plugins/element)
add_subdirectory(plugins/invoker)
add_subdirectory(plugins/ioprocessor)
+SET(WITH_IOPROC_HTTP_TIMEOUT ${WITH_IOPROC_HTTP_TIMEOUT} PARENT_SCOPE)
SET(USCXML_INVOKERS ${USCXML_INVOKERS} PARENT_SCOPE)
SET(USCXML_IOPROCESSORS ${USCXML_IOPROCESSORS} PARENT_SCOPE)
SET(USCXML_ELEMENTS ${USCXML_ELEMENTS} PARENT_SCOPE)
diff --git a/src/uscxml/plugins/ExecutableContentImpl.h b/src/uscxml/plugins/ExecutableContentImpl.h
index 81aedf1..71ceded 100644
--- a/src/uscxml/plugins/ExecutableContentImpl.h
+++ b/src/uscxml/plugins/ExecutableContentImpl.h
@@ -47,10 +47,6 @@ public:
virtual ~ExecutableContentImpl() {};
virtual std::shared_ptr<ExecutableContentImpl> create(InterpreterImpl* interpreter) = 0;
- virtual void setInterpreter(InterpreterImpl* interpreter) {
- _interpreter = interpreter;
- }
-
virtual std::string getLocalName() = 0; ///< The name of the element.
virtual std::string getNamespace() {
return "http://www.w3.org/2005/07/scxml"; ///< The namespace of the element.
diff --git a/src/uscxml/plugins/Factory.cpp b/src/uscxml/plugins/Factory.cpp
index 6ca599c..4c67d1f 100644
--- a/src/uscxml/plugins/Factory.cpp
+++ b/src/uscxml/plugins/Factory.cpp
@@ -56,6 +56,14 @@
# include "uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.h"
#endif
+#ifdef WITH_IOPROC_HTTP
+# include "uscxml/plugins/ioprocessor/http/HTTPIOProcessor.h"
+#endif
+
+#ifdef WITH_ELEMENT_RESPOND
+# include "uscxml/plugins/element/respond/RespondElement.h"
+#endif
+
#include "uscxml/plugins/datamodel/null/NullDataModel.h"
#if defined WITH_DM_ECMA_V8
@@ -181,6 +189,21 @@ void Factory::registerPlugins() {
}
#endif
+#ifdef WITH_IOPROC_HTTP
+ {
+ HTTPIOProcessor* ioProcessor = new HTTPIOProcessor();
+ registerIOProcessor(ioProcessor);
+ }
+#endif
+
+#ifdef WITH_ELEMENT_RESPOND
+ {
+ RespondElement* element = new RespondElement();
+ registerExecutableContent(element);
+ }
+
+#endif
+
#ifdef WITH_DM_ECMA_V8
{
V8DataModel* dataModel = new V8DataModel();
@@ -458,7 +481,6 @@ std::shared_ptr<ExecutableContentImpl> Factory::createExecutableContent(const st
std::string actualNameSpace = (nameSpace.length() == 0 ? "http://www.w3.org/2005/07/scxml" : nameSpace);
if (_executableContent.find(std::make_pair(localName, actualNameSpace)) != _executableContent.end()) {
std::shared_ptr<ExecutableContentImpl> execContent = _executableContent[std::make_pair(localName, actualNameSpace)]->create(interpreter);
- execContent->setInterpreter(interpreter);
return execContent;
}
diff --git a/src/uscxml/plugins/IOProcessor.h b/src/uscxml/plugins/IOProcessor.h
index 55c7109..daff46c 100644
--- a/src/uscxml/plugins/IOProcessor.h
+++ b/src/uscxml/plugins/IOProcessor.h
@@ -46,6 +46,10 @@ public:
virtual bool isValidTarget(const std::string& target);
+ /// Return the shared pointer to the implementation
+ virtual std::shared_ptr<IOProcessorImpl> getImpl() {
+ return _impl;
+ }
protected:
std::shared_ptr<IOProcessorImpl> _impl;
friend class InterpreterImpl;
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 */
diff --git a/src/uscxml/plugins/ioprocessor/CMakeLists.txt b/src/uscxml/plugins/ioprocessor/CMakeLists.txt
index fb4315b..0ba56eb 100644
--- a/src/uscxml/plugins/ioprocessor/CMakeLists.txt
+++ b/src/uscxml/plugins/ioprocessor/CMakeLists.txt
@@ -26,6 +26,7 @@ if (WITH_IOPROC_BASICHTTP)
set(USCXML_IOPROCESSORS "basichttp ${USCXML_IOPROCESSORS}")
file(GLOB_RECURSE BASICHTTP_IOPROCESSOR
basichttp/*.cpp
+ http/*.cpp
basichttp/*.h
)
if (BUILD_AS_PLUGINS)
@@ -42,6 +43,30 @@ if (WITH_IOPROC_BASICHTTP)
endif()
endif()
+
+OPTION(WITH_IOPROC_HTTP "Build the http i/o processor" ON)
+if (WITH_IOPROC_HTTP)
+ SET(WITH_IOPROC_HTTP_TIMEOUT 10)
+ set(USCXML_IOPROCESSORS "http ${USCXML_IOPROCESSORS}")
+ file(GLOB_RECURSE HTTP_IOPROCESSOR
+ http/*.cpp
+ http/*.h
+ )
+ if (BUILD_AS_PLUGINS)
+ source_group("" FILES ${HTTP_IOPROCESSOR})
+ add_library(ioproc_http SHARED ${HTTP_IOPROCESSOR} "../Plugins.cpp")
+ target_link_libraries(ioproc_http
+ uscxml
+ )
+ set_target_properties(ioproc_http PROPERTIES FOLDER "Plugins//IO Processors")
+ set_target_properties(ioproc_http PROPERTIES COMPILE_FLAGS "-DPLUMA_EXPORTS")
+ set_target_properties(ioproc_http PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/plugins")
+ else()
+ list (APPEND USCXML_FILES ${HTTP_IOPROCESSOR})
+ endif()
+endif()
+
+set(WITH_IOPROC_HTTP_TIMEOUT ${WITH_IOPROC_HTTP_TIMEOUT} PARENT_SCOPE)
set(USCXML_INCLUDE_DIRS ${USCXML_INCLUDE_DIRS} PARENT_SCOPE)
set(USCXML_OPT_LIBS ${USCXML_OPT_LIBS} PARENT_SCOPE)
set(USCXML_FILES ${USCXML_FILES} PARENT_SCOPE)
diff --git a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp
index 2f8f0aa..8f3d03e 100644
--- a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp
+++ b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.cpp
@@ -23,10 +23,6 @@
#include "uscxml/messages/Event.h"
#include "uscxml/util/DOM.h"
-#include <event2/dns.h>
-#include <event2/buffer.h>
-#include <event2/keyvalq_struct.h>
-
#include <string.h>
#include "uscxml/interpreter/Logging.h"
@@ -62,12 +58,9 @@ bool pluginConnect(pluma::Host& host) {
// see http://www.w3.org/TR/scxml/#BasicHTTPEventProcessor
BasicHTTPIOProcessor::BasicHTTPIOProcessor() {
- HTTPServer::getInstance();
}
BasicHTTPIOProcessor::~BasicHTTPIOProcessor() {
- HTTPServer* httpServer = HTTPServer::getInstance();
- httpServer->unregisterServlet(this);
}
@@ -87,231 +80,12 @@ std::shared_ptr<IOProcessorImpl> BasicHTTPIOProcessor::create(IOProcessorCallbac
return io;
}
-Data BasicHTTPIOProcessor::getDataModelVariables() {
- Data data;
-
- // we are not connected!
- if(_url.length() == 0)
- return data;
-
- data.compound["location"] = Data(_url, Data::VERBATIM);
-
- URL url(_url);
- data.compound["host"] = Data(url.host(), Data::VERBATIM);
- data.compound["port"] = Data(url.port(), Data::VERBATIM);
- data.compound["path"] = Data(url.path(), Data::VERBATIM);
- data.compound["scheme"] = Data(url.scheme(), Data::VERBATIM);
-
- std::list<std::string> pathComps = url.pathComponents();
- std::list<std::string>::const_iterator pathCompIter = pathComps.begin();
- while(pathCompIter != pathComps.end()) {
- data.compound["pathComponents"].array.push_back(Data(*pathCompIter, Data::VERBATIM));
- pathCompIter++;
- }
-
- return data;
-}
-
bool BasicHTTPIOProcessor::requestFromHTTP(const HTTPServer::Request& req) {
- Event event = req;
- event.eventType = Event::EXTERNAL;
-
-// std::cout << req.raw << std::endl;
-
- /**
- * If a single instance of the parameter '_scxmleventname' is present, the
- * SCXML Processor must use its value as the name of the SCXML event that it
- * raises.
- */
-
- {
- // if we sent ourself an event it will end up here
- // this will call the const subscript operator
- if (req.data.at("content").hasKey("_scxmleventname")) {
- event.name = req.data.at("content").at("_scxmleventname").atom;
- }
- if (req.data.at("content").hasKey("content")) {
- event.data.atom = req.data.at("content").at("content").atom;
- }
- }
-
- // if we used wget, it will end up here - unify?
- if (req.data.hasKey("content")) {
- const Data& data = req.data["content"];
- for(std::map<std::string, Data>::const_iterator compIter = data.compound.begin();
- compIter!= data.compound.end(); compIter++) {
- if (compIter->first == "content") {
- event.data.atom = compIter->second.atom;
- } else {
- event.data[compIter->first] = compIter->second;
- }
- }
- }
-
- if (req.data.hasKey("header")) {
- const Data& data = req.data["header"];
- for(std::map<std::string, Data>::const_iterator compIter = data.compound.begin();
- compIter!= data.compound.end(); compIter++) {
- if (compIter->first == "_scxmleventname") {
- event.name = compIter->second.atom;
- }
- }
- }
-
- // test 532
- if (event.name.length() == 0)
- event.name = "http." + req.data.compound.at("type").atom;
-
- eventToSCXML(event, USCXML_IOPROC_BASICHTTP_TYPE, _url);
+ HTTPIOProcessor::requestFromHTTP(req);
evhttp_send_reply(req.evhttpReq, 200, "OK", NULL);
+ getUnansweredRequests().erase(req.getUUID());
return true;
}
-bool BasicHTTPIOProcessor::isValidTarget(const std::string& target) {
- try {
- URL url(target);
- if (url.scheme().compare("http") != 0)
- return false;
-
- return true;
- } catch (ErrorEvent e) {
- }
- return false;
-}
-
-void BasicHTTPIOProcessor::eventFromSCXML(const std::string& target, const Event& event) {
-
- // TODO: is this still needed with isValidTarget()?
- if (target.length() == 0) {
- _callbacks->enqueueInternal(Event("error.communication", Event::PLATFORM));
- return;
- }
-
- bool isLocal = target == _url;
- URL targetURL(target);
- std::stringstream kvps;
- std::string kvpSeperator;
-
- // event name
- if (event.name.size() > 0) {
- char* eventNameCStr = evhttp_encode_uri("_scxmleventname");
- char* eventValueCStr = evhttp_encode_uri(event.name.c_str());
- kvps << kvpSeperator << eventNameCStr << "=" << eventValueCStr;
- kvpSeperator = "&";
- targetURL.addOutHeader("_scxmleventname", eventValueCStr);
- free(eventNameCStr);
- free(eventValueCStr);
- }
-
- // event namelist
- if (event.namelist.size() > 0) {
- std::map<std::string, Data>::const_iterator namelistIter = event.namelist.begin();
- while (namelistIter != event.namelist.end()) {
- char* keyCStr = evhttp_encode_uri(namelistIter->first.c_str());
- // this is simplified - Data might be more elaborate than a simple string atom
- char* valueCStr = evhttp_encode_uri(namelistIter->second.atom.c_str());
- kvps << kvpSeperator << keyCStr << "=" << valueCStr;
- free(keyCStr);
- free(valueCStr);
- kvpSeperator = "&";
- targetURL.addOutHeader(namelistIter->first, namelistIter->second);
- namelistIter++;
- }
- }
-
- // event params
- if (event.params.size() > 0) {
- std::multimap<std::string, Data>::const_iterator paramIter = event.params.begin();
- while (paramIter != event.params.end()) {
- char* keyCStr = evhttp_encode_uri(paramIter->first.c_str());
- // this is simplified - Data might be more elaborate than a simple string atom
- char* valueCStr = evhttp_encode_uri(paramIter->second.atom.c_str());
- kvps << kvpSeperator << keyCStr << "=" << valueCStr;
- free(keyCStr);
- free(valueCStr);
- kvpSeperator = "&";
- targetURL.addOutHeader(paramIter->first, paramIter->second);
- paramIter++;
- }
- }
-
- // try hard to find actual content
- char* keyCStr = evhttp_encode_uri("content");
- if (!event.data.empty()) {
- char* valueCStr = NULL;
- if (event.data.atom.length() || event.data.array.size() || event.data.compound.size()) {
- valueCStr = evhttp_encode_uri(Data::toJSON(event.data).c_str());
- } else if(event.data.node) {
- std::stringstream xmlStream;
- xmlStream << event.data.node;
- valueCStr = evhttp_encode_uri(xmlStream.str().c_str());
- } else if(event.data.binary) {
- valueCStr = evhttp_encode_uri(event.data.binary.base64().c_str());
- }
- if (valueCStr != NULL) {
- kvps << kvpSeperator << keyCStr << "=" << valueCStr;
- free(valueCStr);
- kvpSeperator = "&";
- }
- }
- free(keyCStr);
-
- targetURL.setOutContent(kvps.str());
- targetURL.addOutHeader("Content-Type", "application/x-www-form-urlencoded");
-
- targetURL.setRequestType(URLRequestType::POST);
- targetURL.addMonitor(this);
-
- _sendRequests[event.sendid] = std::make_pair(targetURL, event);
- if (isLocal) {
- // test201 use a blocking request with local communication
- targetURL.download(true);
- } else {
- URLFetcher::fetchURL(targetURL);
- }
-}
-
-void BasicHTTPIOProcessor::downloadStarted(const URL& url) {}
-
-void BasicHTTPIOProcessor::downloadCompleted(const URL& url) {
- std::map<std::string, std::pair<URL, Event> >::iterator reqIter = _sendRequests.begin();
- while(reqIter != _sendRequests.end()) {
- if (reqIter->second.first == url) {
- // test513
- std::string statusCode = url.getStatusCode();
- if (statusCode.length() > 0) {
- std::string statusPrefix = statusCode.substr(0,1);
- std::string statusRest = statusCode.substr(1);
- Event event;
- event.data = url;
- event.name = "HTTP." + statusPrefix + "." + statusRest;
- eventToSCXML(event, USCXML_IOPROC_BASICHTTP_TYPE, std::string(_url));
- }
- _sendRequests.erase(reqIter);
- return;
- }
- reqIter++;
- }
- assert(false);
-}
-
-void BasicHTTPIOProcessor::downloadFailed(const URL& url, int errorCode) {
-
- std::map<std::string, std::pair<URL, Event> >::iterator reqIter = _sendRequests.begin();
- while(reqIter != _sendRequests.end()) {
- if (reqIter->second.first == url) {
- Event failEvent;
- failEvent.name = "error.communication";
- eventToSCXML(failEvent, USCXML_IOPROC_BASICHTTP_TYPE, std::string(_url));
-
- _sendRequests.erase(reqIter);
- return;
- }
- reqIter++;
- }
- assert(false);
-
-}
-
}
diff --git a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.h b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.h
index bf64111..fb9ed3e 100644
--- a/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.h
+++ b/src/uscxml/plugins/ioprocessor/basichttp/BasicHTTPIOProcessor.h
@@ -42,6 +42,7 @@ extern "C" {
#include "uscxml/server/HTTPServer.h"
#include "uscxml/interpreter/InterpreterImpl.h"
#include "uscxml/plugins/IOProcessorImpl.h"
+#include "uscxml/plugins/ioprocessor/http/HTTPIOProcessor.h"
#ifndef _WIN32
#include <sys/time.h>
@@ -59,7 +60,7 @@ namespace uscxml {
* @ingroup ioproc
* The basichttp I/O processor as per standard.
*/
-class USCXML_PLUGIN_API BasicHTTPIOProcessor : public IOProcessorImpl, public HTTPServlet, public URLMonitor {
+class USCXML_PLUGIN_API BasicHTTPIOProcessor : public HTTPIOProcessor {
public:
BasicHTTPIOProcessor();
virtual ~BasicHTTPIOProcessor();
@@ -72,29 +73,9 @@ public:
return names;
}
- virtual void eventFromSCXML(const std::string& target, const Event& event);
- virtual bool isValidTarget(const std::string& target);
-
- Data getDataModelVariables();
-
/// HTTPServlet
bool requestFromHTTP(const HTTPServer::Request& req);
- void setURL(const std::string& url) {
- _url = url;
- }
-
- bool canAdaptPath() {
- return false;
- }
-
- // URLMonitor
- void downloadStarted(const URL& url);
- void downloadCompleted(const URL& url);
- void downloadFailed(const URL& url, int errorCode);
-protected:
- std::string _url;
- std::map<std::string, std::pair<URL, Event> > _sendRequests;
};
#ifdef BUILD_AS_PLUGINS
diff --git a/src/uscxml/plugins/ioprocessor/http/HTTPIOProcessor.cpp b/src/uscxml/plugins/ioprocessor/http/HTTPIOProcessor.cpp
new file mode 100644
index 0000000..276fd4e
--- /dev/null
+++ b/src/uscxml/plugins/ioprocessor/http/HTTPIOProcessor.cpp
@@ -0,0 +1,329 @@
+/**
+ * @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 "uscxml/Common.h"
+
+#include "uscxml/plugins/ioprocessor/http/HTTPIOProcessor.h"
+#include "uscxml/messages/Event.h"
+#include "uscxml/util/DOM.h"
+
+#include <event2/dns.h>
+#include <event2/buffer.h>
+#include <event2/keyvalq_struct.h>
+
+#include <string.h>
+
+#include "uscxml/interpreter/Logging.h"
+#include <boost/algorithm/string.hpp>
+
+#ifdef _WIN32
+#define NOMINMAX
+#include <winsock2.h>
+#include <windows.h>
+#endif
+
+#ifndef _WIN32
+#include <netdb.h>
+#include <arpa/inet.h>
+#endif
+
+#ifdef BUILD_AS_PLUGINS
+#include <Pluma/Connector.hpp>
+#endif
+
+namespace uscxml {
+
+#ifndef ioprocessor_scxml_EXPORTS
+# ifdef BUILD_AS_PLUGINS
+PLUMA_CONNECTOR
+bool pluginConnect(pluma::Host& host) {
+ host.add( new HTTPIOProcessorProvider() );
+ return true;
+}
+# endif
+#endif
+
+HTTPIOProcessor::HTTPIOProcessor() {
+ HTTPServer::getInstance();
+}
+
+HTTPIOProcessor::~HTTPIOProcessor() {
+ HTTPServer* httpServer = HTTPServer::getInstance();
+ httpServer->unregisterServlet(this);
+}
+
+
+std::shared_ptr<IOProcessorImpl> HTTPIOProcessor::create(IOProcessorCallbacks* callbacks) {
+ std::shared_ptr<HTTPIOProcessor> io(new HTTPIOProcessor());
+ io->_callbacks = callbacks;
+
+ // register at http server
+ std::string path = callbacks->getName();
+ int i = 2;
+ while (!HTTPServer::registerServlet(path, io.get())) {
+ std::stringstream ss;
+ ss << callbacks->getName() << i++;
+ path = ss.str();
+ }
+
+ return io;
+}
+
+Data HTTPIOProcessor::getDataModelVariables() {
+ Data data;
+
+ // we are not connected!
+ if(_url.length() == 0)
+ return data;
+
+ data.compound["location"] = Data(_url, Data::VERBATIM);
+ data.compound["timeout"] = Data(_timeoutS, Data::INTERPRETED);
+
+ URL url(_url);
+ data.compound["host"] = Data(url.host(), Data::VERBATIM);
+ data.compound["port"] = Data(url.port(), Data::VERBATIM);
+ data.compound["path"] = Data(url.path(), Data::VERBATIM);
+ data.compound["scheme"] = Data(url.scheme(), Data::VERBATIM);
+
+ std::list<std::string> pathComps = url.pathComponents();
+ std::list<std::string>::const_iterator pathCompIter = pathComps.begin();
+ while(pathCompIter != pathComps.end()) {
+ data.compound["pathComponents"].array.push_back(Data(*pathCompIter, Data::VERBATIM));
+ pathCompIter++;
+ }
+
+ return data;
+}
+
+bool HTTPIOProcessor::requestFromHTTP(const HTTPServer::Request& req) {
+ Event event = req;
+ event.eventType = Event::EXTERNAL;
+
+ time_t now = std::time(0);
+ _unansweredRequests[req.getUUID()] = std::make_pair(now, req);
+
+ // remove stale requests
+ for (auto reqIter = _unansweredRequests.begin(); reqIter != _unansweredRequests.end();) {
+ if (now > reqIter->second.first + _timeoutS) {
+ evhttp_send_reply(reqIter->second.second.evhttpReq, 504, "Event was not responded to in time", NULL);
+ _unansweredRequests.erase(reqIter++);
+ } else {
+ ++reqIter;
+ }
+ }
+
+ /**
+ * If a single instance of the parameter '_scxmleventname' is present, the
+ * SCXML Processor must use its value as the name of the SCXML event that it
+ * raises.
+ */
+
+ {
+ // if we sent ourself an event it will end up here
+ // this will call the const subscript operator
+ if (req.data.at("content").hasKey("_scxmleventname")) {
+ event.name = req.data.at("content").at("_scxmleventname").atom;
+ }
+ if (req.data.at("content").hasKey("content")) {
+ event.data.atom = req.data.at("content").at("content").atom;
+ }
+ }
+
+ // if we used wget, it will end up here - unify?
+ if (req.data.hasKey("content")) {
+ const Data& data = req.data["content"];
+ for(std::map<std::string, Data>::const_iterator compIter = data.compound.begin();
+ compIter!= data.compound.end(); compIter++) {
+ if (compIter->first == "content") {
+ event.data.atom = compIter->second.atom;
+ } else {
+ event.data[compIter->first] = compIter->second;
+ }
+ }
+ }
+
+ if (req.data.hasKey("header")) {
+ const Data& data = req.data["header"];
+ for(std::map<std::string, Data>::const_iterator compIter = data.compound.begin();
+ compIter!= data.compound.end(); compIter++) {
+ if (compIter->first == "_scxmleventname") {
+ event.name = compIter->second.atom;
+ }
+ }
+ }
+
+ // test 532
+ if (event.name.length() == 0)
+ event.name = "http." + req.data.compound.at("type").atom;
+
+ eventToSCXML(event, USCXML_IOPROC_HTTP_TYPE, req.getUUID());
+
+ // do not reply
+ // evhttp_send_reply(req.evhttpReq, 200, "OK", NULL);
+ return true;
+}
+
+bool HTTPIOProcessor::isValidTarget(const std::string& target) {
+ try {
+ URL url(target);
+ if (url.scheme().compare("http") != 0)
+ return false;
+
+ return true;
+ } catch (ErrorEvent e) {
+ }
+ return false;
+}
+
+void HTTPIOProcessor::eventFromSCXML(const std::string& target, const Event& event) {
+
+ // TODO: is this still needed with isValidTarget()?
+ if (target.length() == 0) {
+ _callbacks->enqueueInternal(Event("error.communication", Event::PLATFORM));
+ return;
+ }
+
+ bool isLocal = target == _url;
+ URL targetURL(target);
+ std::stringstream kvps;
+ std::string kvpSeperator;
+
+ // event name
+ if (event.name.size() > 0) {
+ char* eventNameCStr = evhttp_encode_uri("_scxmleventname");
+ char* eventValueCStr = evhttp_encode_uri(event.name.c_str());
+ kvps << kvpSeperator << eventNameCStr << "=" << eventValueCStr;
+ kvpSeperator = "&";
+ targetURL.addOutHeader("_scxmleventname", eventValueCStr);
+ free(eventNameCStr);
+ free(eventValueCStr);
+ }
+
+ // event namelist
+ if (event.namelist.size() > 0) {
+ std::map<std::string, Data>::const_iterator namelistIter = event.namelist.begin();
+ while (namelistIter != event.namelist.end()) {
+ char* keyCStr = evhttp_encode_uri(namelistIter->first.c_str());
+ // this is simplified - Data might be more elaborate than a simple string atom
+ char* valueCStr = evhttp_encode_uri(namelistIter->second.atom.c_str());
+ kvps << kvpSeperator << keyCStr << "=" << valueCStr;
+ free(keyCStr);
+ free(valueCStr);
+ kvpSeperator = "&";
+ targetURL.addOutHeader(namelistIter->first, namelistIter->second);
+ namelistIter++;
+ }
+ }
+
+ // event params
+ if (event.params.size() > 0) {
+ std::multimap<std::string, Data>::const_iterator paramIter = event.params.begin();
+ while (paramIter != event.params.end()) {
+ char* keyCStr = evhttp_encode_uri(paramIter->first.c_str());
+ // this is simplified - Data might be more elaborate than a simple string atom
+ char* valueCStr = evhttp_encode_uri(paramIter->second.atom.c_str());
+ kvps << kvpSeperator << keyCStr << "=" << valueCStr;
+ free(keyCStr);
+ free(valueCStr);
+ kvpSeperator = "&";
+ targetURL.addOutHeader(paramIter->first, paramIter->second);
+ paramIter++;
+ }
+ }
+
+ // try hard to find actual content
+ char* keyCStr = evhttp_encode_uri("content");
+ if (!event.data.empty()) {
+ char* valueCStr = NULL;
+ if (event.data.atom.length() || event.data.array.size() || event.data.compound.size()) {
+ valueCStr = evhttp_encode_uri(Data::toJSON(event.data).c_str());
+ } else if(event.data.node) {
+ std::stringstream xmlStream;
+ xmlStream << event.data.node;
+ valueCStr = evhttp_encode_uri(xmlStream.str().c_str());
+ } else if(event.data.binary) {
+ valueCStr = evhttp_encode_uri(event.data.binary.base64().c_str());
+ }
+ if (valueCStr != NULL) {
+ kvps << kvpSeperator << keyCStr << "=" << valueCStr;
+ free(valueCStr);
+ kvpSeperator = "&";
+ }
+ }
+ free(keyCStr);
+
+ targetURL.setOutContent(kvps.str());
+ targetURL.addOutHeader("Content-Type", "application/x-www-form-urlencoded");
+
+ targetURL.setRequestType(URLRequestType::POST);
+ targetURL.addMonitor(this);
+
+ _sendRequests[event.sendid] = std::make_pair(targetURL, event);
+ if (isLocal) {
+ // test201 use a blocking request with local communication
+ targetURL.download(true);
+ } else {
+ URLFetcher::fetchURL(targetURL);
+ }
+}
+
+void HTTPIOProcessor::downloadStarted(const URL& url) {}
+
+void HTTPIOProcessor::downloadCompleted(const URL& url) {
+ std::map<std::string, std::pair<URL, Event> >::iterator reqIter = _sendRequests.begin();
+ while(reqIter != _sendRequests.end()) {
+ if (reqIter->second.first == url) {
+ // test513
+ std::string statusCode = url.getStatusCode();
+ if (statusCode.length() > 0) {
+ std::string statusPrefix = statusCode.substr(0,1);
+ std::string statusRest = statusCode.substr(1);
+ Event event;
+ event.data = url;
+ event.name = "HTTP." + statusPrefix + "." + statusRest;
+ eventToSCXML(event, USCXML_IOPROC_HTTP_TYPE, std::string(_url));
+ }
+ _sendRequests.erase(reqIter);
+ return;
+ }
+ reqIter++;
+ }
+ assert(false);
+}
+
+void HTTPIOProcessor::downloadFailed(const URL& url, int errorCode) {
+
+ std::map<std::string, std::pair<URL, Event> >::iterator reqIter = _sendRequests.begin();
+ while(reqIter != _sendRequests.end()) {
+ if (reqIter->second.first == url) {
+ Event failEvent;
+ failEvent.name = "error.communication";
+ eventToSCXML(failEvent, USCXML_IOPROC_HTTP_TYPE, std::string(_url));
+
+ _sendRequests.erase(reqIter);
+ return;
+ }
+ reqIter++;
+ }
+ assert(false);
+
+}
+
+
+}
diff --git a/src/uscxml/plugins/ioprocessor/http/HTTPIOProcessor.h b/src/uscxml/plugins/ioprocessor/http/HTTPIOProcessor.h
new file mode 100644
index 0000000..631af27
--- /dev/null
+++ b/src/uscxml/plugins/ioprocessor/http/HTTPIOProcessor.h
@@ -0,0 +1,115 @@
+/**
+ * @file
+ * @author 2012-2013 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 HTTPIOPROCESSOR_H_645835
+#define HTTPIOPROCESSOR_H_645835
+
+#include "uscxml/config.h"
+
+extern "C" {
+#include <event2/http.h>
+#include <event2/http_struct.h>
+}
+
+#include <chrono>
+
+// why is it duplicated from Common.h here?
+
+#if defined(_WIN32) && !defined(USCXML_STATIC)
+# if (defined ioprocessor_http_EXPORTS || defined USCXML_EXPORT)
+# define USCXML_PLUGIN_API __declspec(dllexport)
+# else
+# define USCXML_PLUGIN_API __declspec(dllimport)
+# endif
+#else
+# define USCXML_PLUGIN_API
+#endif
+
+#include "uscxml/server/HTTPServer.h"
+#include "uscxml/interpreter/InterpreterImpl.h"
+#include "uscxml/plugins/IOProcessorImpl.h"
+
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
+
+#ifdef BUILD_AS_PLUGINS
+#include "uscxml/plugins/Plugins.h"
+#endif
+
+#define USCXML_IOPROC_HTTP_TYPE "http://www.w3.org/TR/scxml/#HTTPEventProcessor"
+
+namespace uscxml {
+
+/**
+ * @ingroup ioproc
+ * The http I/O processor.
+ */
+class USCXML_PLUGIN_API HTTPIOProcessor : public IOProcessorImpl, public HTTPServlet, public URLMonitor {
+public:
+ HTTPIOProcessor();
+ virtual ~HTTPIOProcessor();
+ virtual std::shared_ptr<IOProcessorImpl> create(uscxml::IOProcessorCallbacks* callbacks);
+
+ virtual std::list<std::string> getNames() {
+ std::list<std::string> names;
+ names.push_back("http");
+ names.push_back(USCXML_IOPROC_HTTP_TYPE);
+ return names;
+ }
+
+ virtual void eventFromSCXML(const std::string& target, const Event& event);
+ virtual bool isValidTarget(const std::string& target);
+
+ Data getDataModelVariables();
+
+ /// HTTPServlet
+ bool requestFromHTTP(const HTTPServer::Request& req);
+ void setURL(const std::string& url) {
+ _url = url;
+ }
+
+ bool canAdaptPath() {
+ return true;
+ }
+
+ // URLMonitor
+ void downloadStarted(const URL& url);
+ void downloadCompleted(const URL& url);
+ void downloadFailed(const URL& url, int errorCode);
+
+ std::map<std::string, std::pair<std::time_t, HTTPServer::Request> >& getUnansweredRequests() {
+ return _unansweredRequests;
+ }
+
+protected:
+ std::string _url;
+ size_t _timeoutS = WITH_IOPROC_HTTP_TIMEOUT;
+ std::map<std::string, std::pair<URL, Event> > _sendRequests;
+ std::map<std::string, std::pair<std::time_t, HTTPServer::Request> > _unansweredRequests;
+
+};
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_INHERIT_PROVIDER(HTTPIOProcessor, IOProcessorImpl)
+#endif
+
+}
+
+#endif /* end of include guard: HTTPIOPROCESSOR_H_645835 */
diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp
index faf91d8..cee8325 100644
--- a/src/uscxml/server/HTTPServer.cpp
+++ b/src/uscxml/server/HTTPServer.cpp
@@ -72,12 +72,12 @@ extern "C" {
namespace uscxml {
static void dummyCallback(evutil_socket_t fd, short what, void *arg) {
- // see comments in BasicDelayedEventQueue::run
- timeval tv;
- tv.tv_sec = 365 * 24 * 3600;
- tv.tv_usec = 0;
- event *ev = *(event **)arg;
- evtimer_add(ev, &tv);
+ // see comments in BasicDelayedEventQueue::run
+ timeval tv;
+ tv.tv_sec = 365 * 24 * 3600;
+ tv.tv_usec = 0;
+ event *ev = *(event **)arg;
+ evtimer_add(ev, &tv);
}
HTTPServer::HTTPServer(unsigned short port, unsigned short wsPort, SSLConfig* sslConf) {
@@ -88,11 +88,11 @@ HTTPServer::HTTPServer(unsigned short port, unsigned short wsPort, SSLConfig* ss
_thread = NULL;
_httpHandle = NULL;
- timeval tv;
- tv.tv_sec = 365 * 24 * 3600;
- tv.tv_usec = 0;
- _dummyEvent = evtimer_new(_base, dummyCallback, &_dummyEvent);
- evtimer_add(_dummyEvent, &tv);
+ timeval tv;
+ tv.tv_sec = 365 * 24 * 3600;
+ tv.tv_usec = 0;
+ _dummyEvent = evtimer_new(_base, dummyCallback, &_dummyEvent);
+ evtimer_add(_dummyEvent, &tv);
#ifdef _WIN32
_wsHandle = NULL;
@@ -755,10 +755,10 @@ void HTTPServer::run(void* instance) {
// event_base_loop(INSTANCE->_base, EVLOOP_ONCE | EVLOOP_NO_EXIT_ON_EMPTY);
// event_base_dispatch(INSTANCE->_base);
- // BasicDelayedEventQueue::run
- event_base_loop(INSTANCE->_base, EVLOOP_ONCE);
+ // BasicDelayedEventQueue::run
+ event_base_loop(INSTANCE->_base, EVLOOP_ONCE);
- }
+ }
LOGD(USCXML_INFO) << "HTTP Server stopped" << std::endl;
}
diff --git a/src/uscxml/server/HTTPServer.h b/src/uscxml/server/HTTPServer.h
index 3d7f6b4..84caf9e 100644
--- a/src/uscxml/server/HTTPServer.h
+++ b/src/uscxml/server/HTTPServer.h
@@ -169,7 +169,7 @@ private:
struct event_base* _base;
struct evhttp* _http;
struct evws* _evws;
- struct event* _dummyEvent;
+ struct event* _dummyEvent;
struct evhttp_bound_socket* _httpHandle;
evutil_socket_t _wsHandle;
diff --git a/src/uscxml/util/Predicates.cpp b/src/uscxml/util/Predicates.cpp
index c4effdb..3f6128f 100644
--- a/src/uscxml/util/Predicates.cpp
+++ b/src/uscxml/util/Predicates.cpp
@@ -325,6 +325,8 @@ DOMElement* getState(const std::string& stateId, const DOMElement* root) {
stateStack.insert(stateStack.end(), children.begin(), children.end());
}
+ // no state with such an id in document!
+ assert(false);
return NULL;
}
diff --git a/src/uscxml/util/URL.cpp b/src/uscxml/util/URL.cpp
index b429e22..e64139c 100644
--- a/src/uscxml/util/URL.cpp
+++ b/src/uscxml/util/URL.cpp
@@ -144,6 +144,34 @@ std::string URL::getTempDir(bool shared) {
#endif
}
+std::map<std::string, std::string> URL::mimeTypes;
+std::string URL::getMimeType(const std::string extension, std::string magic) {
+ if (mimeTypes.empty()) {
+ mimeTypes["txt"] = "text/plain";
+ mimeTypes["c"] = "text/plain";
+ mimeTypes["h"] = "text/plain";
+ mimeTypes["html"] = "text/html";
+ mimeTypes["htm"] = "text/htm";
+ mimeTypes["css"] = "text/css";
+ mimeTypes["bmp"] = "image/bmp";
+ mimeTypes["gif"] = "image/gif";
+ mimeTypes["jpg"] = "image/jpeg";
+ mimeTypes["jpeg"] = "image/jpeg";
+ mimeTypes["mpg"] = "video/mpeg";
+ mimeTypes["mov"] = "video/quicktime";
+ mimeTypes["png"] = "image/png";
+ mimeTypes["pdf"] = "application/pdf";
+ mimeTypes["ps"] = "application/postscript";
+ mimeTypes["tif"] = "image/tiff";
+ mimeTypes["tiff"] = "image/tiff";
+ }
+
+ if (mimeTypes.find(extension) != mimeTypes.end()) {
+ return mimeTypes[extension];
+ }
+ return "application/octet-stream";
+}
+
// Version for MacOSX in URL.mm
#if (!defined APPLE && !defined IOS)
std::string URL::getResourceDir() {
diff --git a/src/uscxml/util/URL.h b/src/uscxml/util/URL.h
index 9ad2f8e..f9e7ed5 100644
--- a/src/uscxml/util/URL.h
+++ b/src/uscxml/util/URL.h
@@ -149,6 +149,8 @@ public:
*/
static std::string getTempDir(bool shared = true);
+ static std::string getMimeType(const std::string extension, std::string magic = "");
+
bool isAbsolute() {
return _impl->isAbsolute();
}
@@ -253,6 +255,7 @@ public:
protected:
std::shared_ptr<URLImpl> _impl;
+ static std::map<std::string, std::string> mimeTypes;
friend class URLFetcher;
static std::string currTmpDir;
diff --git a/test/w3c/manual/test-http.scxml b/test/w3c/manual/test-http.scxml
new file mode 100644
index 0000000..2a18141
--- /dev/null
+++ b/test/w3c/manual/test-http.scxml
@@ -0,0 +1,14 @@
+<scxml version="1.0" datamodel="ecmascript" xmlns="http://www.w3.org/2005/07/scxml" xmlns:conf="http://www.w3.org/2005/scxml-conformance">
+ <state id="start">
+ <onentry>
+ <log label="Listening at" expr="_ioprocessors['http']['location']" />
+ </onentry>
+ <state id="loop">
+ <transition event="http" target="loop">
+ <log expr="_event" />
+ <respond status="200" to="_event.origin" />
+ </transition>
+ </state>
+ </state>
+ <final id="final" />
+</scxml> \ No newline at end of file