diff options
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 |