diff options
Diffstat (limited to 'src/uscxml/plugins/ioprocessor')
5 files changed, 474 insertions, 249 deletions
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..aefc771 --- /dev/null +++ b/src/uscxml/plugins/ioprocessor/http/HTTPIOProcessor.h @@ -0,0 +1,116 @@ +/** + * @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> +#include <ctime> + +// 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 */ |