diff --git a/hueplusplus/include/HttpHandler.h b/hueplusplus/include/HttpHandler.h deleted file mode 100755 index a5ea2b5..0000000 --- a/hueplusplus/include/HttpHandler.h +++ /dev/null @@ -1,158 +0,0 @@ -/** - \file HttpHandler.h - Copyright Notice\n - Copyright (C) 2017 Jan Rogall - developer\n - Copyright (C) 2017 Moritz Wirger - developer\n - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - 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. See the - GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -**/ - -#ifndef _HTTPHANDLER_H -#define _HTTPHANDLER_H - -#include -#include - -#include "IHttpHandler.h" - -#include "json/json.h" -#include "IHttpHandler.h" - -//! Class to handle http requests and multicast requests -class HttpHandler : public IHttpHandler -{ -public: - //! \brief Function that sends a given message to the specified host and returns the response. - //! - //! \param msg String that contains the message that is sent to the specified address - //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" - //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 - //! \return String containing the response of the host - std::string send(const std::string &msg, const std::string &adr, int port=80) const; - - //! \brief Function that sends a given message to the specified host and returns the body of the response. - //! - //! Note if no body is found a runtime error is thrown! - //! \param msg String that contains the message that is sent to the specified address - //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" - //! \param port Optional integer that specifies the port to which the request is sent. Default is 80 - //! \return String containing the body of the response of the host - std::string sendGetHTTPBody(const std::string &msg, const std::string &adr, int port = 80) const; - - //! \brief Function that sends a multicast request with the specified message. - //! - //! \param msg String that contains the request that is sent to the specified address - //! \param adr Optional String that contains an ip or hostname in dotted decimal notation, default is "239.255.255.250" - //! \param port Optional integer that specifies the port to which the request is sent. Default is 1900 - //! \param timeout Optional Integer that specifies the timeout of the request in seconds. Default is 5 - //! \return Vector containing strings of each answer received - std::vector sendMulticast(const std::string &msg, const std::string &adr = "239.255.255.250", int port = 1900, int timeout = 5) const; - - //! \brief Function that sends a HTTP request with the given method to the specified host and returns the body of the response. - //! - //! Note body can also be left empty! - //! \param method String that contains the HTTP method type e.g. GET, HEAD, POST, PUT, DELETE, ... - //! \param uri String that contains the uniform resource identifier - //! \param content_type String that contains the type(MIME) of the body data e.g. "text/html", "application/json", ... - //! \param body String that contains the data of the request - //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" - //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 - //! \return String containing the body of the response of the host - std::string sendHTTPRequest(std::string method, std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const; - - //! \brief Function that sends a HTTP GET request to the specified host and returns the body of the response. - //! - //! Note body can also be left empty! - //! \param uri String that contains the uniform resource identifier - //! \param content_type String that contains the type(MIME) of the body data e.g. "text/html", "application/json", ... - //! \param body String that contains the data of the request - //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" - //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 - //! \return String containing the body of the response of the host - std::string GETString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const; - - //! \brief Function that sends a HTTP POST request to the specified host and returns the body of the response. - //! - //! Note body can also be left empty! - //! \param uri String that contains the uniform resource identifier - //! \param content_type String that contains the type(MIME) of the body data e.g. "text/html", "application/json", ... - //! \param body String that contains the data of the request - //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" - //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 - //! \return String containing the body of the response of the host - std::string POSTString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const; - - //! \brief Function that sends a HTTP PUT request to the specified host and returns the body of the response. - //! - //! Note body can also be left empty! - //! \param uri String that contains the uniform resource identifier - //! \param content_type String that contains the type(MIME) of the body data e.g. "text/html", "application/json", ... - //! \param body String that contains the data of the request - //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" - //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 - //! \return String containing the body of the response of the host - std::string PUTString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const; - - //! \brief Function that sends a HTTP DELETE request to the specified host and returns the body of the response. - //! - //! Note body can also be left empty! - //! \param uri String that contains the uniform resource identifier - //! \param content_type String that contains the type(MIME) of the body data e.g. "text/html", "application/json", ... - //! \param body String that contains the data of the request - //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" - //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 - //! \return String containing the body of the response of the host - std::string DELETEString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const; - - //! \brief Function that sends a HTTP GET request to the specified host and returns the body of the response. - //! - //! Note body can also be left empty! - //! \param uri String that contains the uniform resource identifier - //! \param body Json::Value that contains the data of the request - //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" - //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 - //! \return Json::Value containing the parsed body of the response of the host - Json::Value GETJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const; - - //! \brief Function that sends a HTTP POST request to the specified host and returns the body of the response. - //! - //! Note body can also be left empty! - //! \param uri String that contains the uniform resource identifier - //! \param body Json::Value that contains the data of the request - //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" - //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 - //! \return Json::Value containing the parsed body of the response of the host - Json::Value POSTJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const; - - //! \brief Function that sends a HTTP PUT request to the specified host and returns the body of the response. - //! - //! Note body can also be left empty! - //! \param uri String that contains the uniform resource identifier - //! \param body Json::Value that contains the data of the request - //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" - //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 - //! \return Json::Value containing the parsed body of the response of the host - Json::Value PUTJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const; - - //! \brief Function that sends a HTTP DELETE request to the specified host and returns the body of the response. - //! - //! Note body can also be left empty! - //! \param uri String that contains the uniform resource identifier - //! \param body Json::Value that contains the data of the request - //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" - //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 - //! \return Json::Value containing the parsed body of the response of the host - Json::Value DELETEJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const; -}; - -#endif diff --git a/hueplusplus/include/IHttpHandler.h b/hueplusplus/include/IHttpHandler.h index 885f298..6845222 100644 --- a/hueplusplus/include/IHttpHandler.h +++ b/hueplusplus/include/IHttpHandler.h @@ -20,6 +20,8 @@ #ifndef _IHTTPHANDLER_H #define _IHTTPHANDLER_H +#include +#include #include #include @@ -48,7 +50,18 @@ public: //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" //! \param port Optional integer that specifies the port to which the request should be sent. Default is 80 //! \return String containing the body of the response of the host - virtual std::string sendGetHTTPBody(const std::string &msg, const std::string &adr, int port = 80) const = 0; + virtual std::string sendGetHTTPBody(const std::string &msg, const std::string &adr, int port = 80) const + { + std::string response = send(msg, adr, port); + size_t start = response.find("\r\n\r\n"); + if (start == std::string::npos) + { + std::cerr << "Failed to find body in response\n"; + throw(std::runtime_error("Failed to find body in response")); + } + response.erase(0, start + 4); + return response; + }; //! \brief Virtual function that should send a multicast request with a specified message. //! @@ -69,7 +82,31 @@ public: //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 //! \return String containing the body of the response of the host - virtual std::string sendHTTPRequest(std::string method, std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const = 0; + virtual std::string sendHTTPRequest(std::string method, std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const + { + std::string request; + // Protocol reference: https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html + // Request-Line + request.append(method); // Method + request.append(" "); // Separation + request.append(uri); // Request-URI + request.append(" "); // Separation + request.append("HTTP/1.0"); // HTTP-Version + request.append("\r\n"); // Ending + // Entities + request.append("Content-Type:"); // entity-header + request.append(" "); // Separation + request.append(content_type); // media-type + request.append("\r\n"); // Entity ending + request.append("Content-Length:"); // entity-header + request.append(" "); // Separation + request.append(std::to_string(body.size())); // length + request.append("\r\n\r\n"); // Entity ending & Request-Line ending + request.append(body); // message-body + request.append("\r\n\r\n"); // Ending + + return sendGetHTTPBody(request.c_str(), adr, port); + }; //! \brief Virtual function that should send a HTTP GET request to the specified host and return the body of the response. //! @@ -80,7 +117,10 @@ public: //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 //! \return String containing the body of the response of the host - virtual std::string GETString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const = 0; + virtual std::string GETString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const + { + return sendHTTPRequest("GET", uri, content_type, body, adr, port); + }; //! \brief Virtual function that should send a HTTP POST request to the specified host and returns the body of the response. //! @@ -91,7 +131,10 @@ public: //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 //! \return String containing the body of the response of the host - virtual std::string POSTString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const = 0; + virtual std::string POSTString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const + { + return sendHTTPRequest("POST", uri, content_type, body, adr, port); + }; //! \brief Virtual function that should send a HTTP PUT request to the specified host and return the body of the response. //! @@ -102,7 +145,10 @@ public: //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 //! \return String containing the body of the response of the host - virtual std::string PUTString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const = 0; + virtual std::string PUTString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const + { + return sendHTTPRequest("PUT", uri, content_type, body, adr, port); + }; //! \brief Virtual function that should send a HTTP DELETE request to the specified host and return the body of the response. //! @@ -113,7 +159,10 @@ public: //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 //! \return String containing the body of the response of the host - virtual std::string DELETEString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const = 0; + virtual std::string DELETEString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const + { + return sendHTTPRequest("DELETE", uri, content_type, body, adr, port); + }; //! \brief Virtual function that should send a HTTP GET request to the specified host and return the body of the response. //! @@ -123,7 +172,22 @@ public: //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 //! \return Json::Value containing the parsed body of the response of the host - virtual Json::Value GETJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const = 0; + virtual Json::Value GETJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const + { + std::string response = GETString(uri, "application/json", body.toStyledString(), adr, port); + + std::string error; + Json::Value result; + Json::CharReaderBuilder builder; + builder["collectComments"] = false; + std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); + if (!reader->parse(response.c_str(), response.c_str() + response.length(), &result, &error)) + { + std::cout << "Error while parsing JSON in function GETJson() of linHttpHandler: " << error << std::endl; + throw(std::runtime_error("Error while parsing JSON in function GETJson() of linHttpHandler")); + } + return result; + }; //! \brief Virtual function that should send a HTTP POST request to the specified host and return the body of the response. //! @@ -133,7 +197,22 @@ public: //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 //! \return Json::Value containing the parsed body of the response of the host - virtual Json::Value POSTJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const = 0; + virtual Json::Value POSTJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const + { + std::string response = POSTString(uri, "application/json", body.toStyledString(), adr, port); + + std::string error; + Json::Value result; + Json::CharReaderBuilder builder; + builder["collectComments"] = false; + std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); + if (!reader->parse(response.c_str(), response.c_str() + response.length(), &result, &error)) + { + std::cout << "Error while parsing JSON in function POSTJson() of linHttpHandler: " << error << std::endl; + throw(std::runtime_error("Error while parsing JSON in function POSTJson() of linHttpHandler")); + } + return result; + } //! \brief Virtual function that should send a HTTP PUT request to the specified host and return the body of the response. //! @@ -143,7 +222,22 @@ public: //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 //! \return Json::Value containing the parsed body of the response of the host - virtual Json::Value PUTJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const = 0; + virtual Json::Value PUTJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const + { + std::string response = PUTString(uri, "application/json", body.toStyledString(), adr, port); + + std::string error; + Json::Value result; + Json::CharReaderBuilder builder; + builder["collectComments"] = false; + std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); + if (!reader->parse(response.c_str(), response.c_str() + response.length(), &result, &error)) + { + std::cout << "Error while parsing JSON in function PUTJson() of linHttpHandler: " << error << std::endl; + throw(std::runtime_error("Error while parsing JSON in function PUTJson() of linHttpHandler")); + } + return result; + }; //! \brief Virtual function that should send a HTTP DELETE request to the specified host and return the body of the response. //! @@ -153,7 +247,22 @@ public: //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 //! \return Json::Value containing the parsed body of the response of the host - virtual Json::Value DELETEJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const = 0; + virtual Json::Value DELETEJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const + { + std::string response = DELETEString(uri, "application/json", body.toStyledString(), adr, port); + + std::string error; + Json::Value result; + Json::CharReaderBuilder builder; + builder["collectComments"] = false; + std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); + if (!reader->parse(response.c_str(), response.c_str() + response.length(), &result, &error)) + { + std::cout << "Error while parsing JSON in function PUTJson() of linHttpHandler: " << error << std::endl; + throw(std::runtime_error("Error while parsing JSON in function PUTJson() of linHttpHandler")); + } + return result; + }; }; #endif diff --git a/hueplusplus/include/linHttpHandler.h b/hueplusplus/include/linHttpHandler.h new file mode 100644 index 0000000..1a82342 --- /dev/null +++ b/hueplusplus/include/linHttpHandler.h @@ -0,0 +1,52 @@ +/** + \file linHttpHandler.h + Copyright Notice\n + Copyright (C) 2017 Jan Rogall - developer\n + Copyright (C) 2017 Moritz Wirger - developer\n + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + 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. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**/ + +#ifndef _LINHTTPHANDLER_H +#define _LINHTTPHANDLER_H + +#include +#include + +#include "IHttpHandler.h" + +#include "json/json.h" + +//! Class to handle http requests and multicast requests on linux systems +class linHttpHandler : public IHttpHandler +{ +public: + //! \brief Function that sends a given message to the specified host and returns the response. + //! + //! \param msg String that contains the message that is sent to the specified address + //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" + //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 + //! \return String containing the response of the host + std::string send(const std::string &msg, const std::string &adr, int port=80) const; + + //! \brief Function that sends a multicast request with the specified message. + //! + //! \param msg String that contains the request that is sent to the specified address + //! \param adr Optional String that contains an ip or hostname in dotted decimal notation, default is "239.255.255.250" + //! \param port Optional integer that specifies the port to which the request is sent. Default is 1900 + //! \param timeout Optional Integer that specifies the timeout of the request in seconds. Default is 5 + //! \return Vector containing strings of each answer received + std::vector sendMulticast(const std::string &msg, const std::string &adr = "239.255.255.250", int port = 1900, int timeout = 5) const; +}; + +#endif diff --git a/hueplusplus/include/winHttpHandler.h b/hueplusplus/include/winHttpHandler.h new file mode 100644 index 0000000..56642ef --- /dev/null +++ b/hueplusplus/include/winHttpHandler.h @@ -0,0 +1,62 @@ +/** +\file winHttpHandler.h +Copyright Notice\n +Copyright (C) 2017 Jan Rogall - developer\n +Copyright (C) 2017 Moritz Wirger - developer\n + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. +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. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**/ + +#ifndef _WINHTTPHANDLER_H +#define _WINHTTPHANDLER_H + +#include +#include +#include + +#include "IHttpHandler.h" + +#include "json/json.h" + +//! Class to handle http requests and multicast requests on windows systems +class winHttpHandler : public IHttpHandler +{ +public: + //! \brief Ctor needed for initializing wsaData + winHttpHandler(); + + //! \brief Dtor needed for wsaData cleanup + ~winHttpHandler(); + + //! \brief Function that sends a given message to the specified host and returns the response. + //! + //! \param msg String that contains the message that is sent to the specified address + //! \param adr String that contains an ip or hostname in dotted decimal notation like "192.168.2.1" + //! \param port Optional integer that specifies the port to which the request is sent to. Default is 80 + //! \return String containing the response of the host + std::string send(const std::string &msg, const std::string &adr, int port = 80) const; + + //! \brief Function that sends a multicast request with the specified message. + //! + //! \param msg String that contains the request that is sent to the specified address + //! \param adr Optional String that contains an ip or hostname in dotted decimal notation, default is "239.255.255.250" + //! \param port Optional integer that specifies the port to which the request is sent. Default is 1900 + //! \param timeout Optional Integer that specifies the timeout of the request in seconds. Default is 5 + //! \return Vector containing strings of each answer received + std::vector sendMulticast(const std::string &msg, const std::string &adr = "239.255.255.250", int port = 1900, int timeout = 5) const; + +private: + WSADATA wsaData; +}; + +#endif diff --git a/hueplusplus/HttpHandler.cpp b/hueplusplus/linHttpHandler.cpp old mode 100755 new mode 100644 index 0ccf199..90b6460 --- a/hueplusplus/HttpHandler.cpp +++ b/hueplusplus/linHttpHandler.cpp @@ -1,5 +1,5 @@ /** - \file HttpHandler.cpp + \file linHttpHandler.cpp Copyright Notice\n Copyright (C) 2017 Jan Rogall - developer\n Copyright (C) 2017 Moritz Wirger - developer\n @@ -17,7 +17,9 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **/ -#include "include/HttpHandler.h" +#include "include/linHttpHandler.h" + +#ifndef _MSC_VER #include #include // struct sockaddr_in, struct sockaddr @@ -38,7 +40,7 @@ class SocketCloser { private: int s; }; -std::string HttpHandler::send(const std::string & msg, const std::string & adr, int port) const +std::string linHttpHandler::send(const std::string & msg, const std::string & adr, int port) const { // create socket int socketFD = socket(AF_INET, SOCK_STREAM, 0); @@ -127,20 +129,7 @@ std::string HttpHandler::send(const std::string & msg, const std::string & adr, return response; } -std::string HttpHandler::sendGetHTTPBody(const std::string & msg, const std::string & adr, int port) const -{ - std::string response = send(msg, adr, port); - size_t start = response.find("\r\n\r\n"); - if (start == std::string::npos) - { - std::cerr << "Failed to find body in response\n"; - throw(std::runtime_error("Failed to find body in response")); - } - response.erase(0, start + 4); - return response; -} - -std::vector HttpHandler::sendMulticast(const std::string & msg, const std::string & adr, int port, int timeout) const +std::vector linHttpHandler::sendMulticast(const std::string & msg, const std::string & adr, int port, int timeout) const { hostent *server; // host information sockaddr_in server_addr; // server address @@ -219,116 +208,4 @@ std::vector HttpHandler::sendMulticast(const std::string & msg, con return returnString; } -std::string HttpHandler::sendHTTPRequest(std::string method, std::string uri, std::string content_type, std::string body, const std::string &adr, int port) const -{ - std::string request; - // Protocol reference: https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html - // Request-Line - request.append(method); // Method - request.append(" "); // Separation - request.append(uri); // Request-URI - request.append(" "); // Separation - request.append("HTTP/1.0"); // HTTP-Version - request.append("\r\n"); // Ending - // Entities - request.append("Content-Type:"); // entity-header - request.append(" "); // Separation - request.append(content_type); // media-type - request.append("\r\n"); // Entity ending - request.append("Content-Length:"); // entity-header - request.append(" "); // Separation - request.append(std::to_string(body.size())); // length - request.append("\r\n\r\n"); // Entity ending & Request-Line ending - request.append(body); // message-body - request.append("\r\n\r\n"); // Ending - - return sendGetHTTPBody(request.c_str(), adr, port); -} - -std::string HttpHandler::GETString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port) const -{ - return sendHTTPRequest("GET", uri, content_type, body, adr, port); -} - -std::string HttpHandler::POSTString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port) const -{ - return sendHTTPRequest("POST", uri, content_type, body, adr, port); -} - -std::string HttpHandler::PUTString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port) const -{ - return sendHTTPRequest("PUT", uri, content_type, body, adr, port); -} - -std::string HttpHandler::DELETEString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port) const -{ - return sendHTTPRequest("DELETE", uri, content_type, body, adr, port); -} - -Json::Value HttpHandler::GETJson(std::string uri, const Json::Value& body, const std::string &adr, int port) const -{ - std::string response = GETString(uri, "application/json", body.toStyledString(), adr, port); - - std::string error; - Json::Value result; - Json::CharReaderBuilder builder; - builder["collectComments"] = false; - std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); - if (!reader->parse(response.c_str(), response.c_str() + response.length(), &result, &error)) - { - std::cout << "Error while parsing JSON in function GETJson() of HttpHandler: " << error << std::endl; - throw(std::runtime_error("Error while parsing JSON in function GETJson() of HttpHandler")); - } - return result; -} - -Json::Value HttpHandler::POSTJson(std::string uri, const Json::Value& body, const std::string &adr, int port) const -{ - std::string response = POSTString(uri, "application/json", body.toStyledString(), adr, port); - - std::string error; - Json::Value result; - Json::CharReaderBuilder builder; - builder["collectComments"] = false; - std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); - if (!reader->parse(response.c_str(), response.c_str() + response.length(), &result, &error)) - { - std::cout << "Error while parsing JSON in function POSTJson() of HttpHandler: " << error << std::endl; - throw(std::runtime_error("Error while parsing JSON in function POSTJson() of HttpHandler")); - } - return result; -} - -Json::Value HttpHandler::PUTJson(std::string uri, const Json::Value& body, const std::string &adr, int port) const -{ - std::string response = PUTString(uri, "application/json", body.toStyledString(), adr, port); - - std::string error; - Json::Value result; - Json::CharReaderBuilder builder; - builder["collectComments"] = false; - std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); - if (!reader->parse(response.c_str(), response.c_str() + response.length(), &result, &error)) - { - std::cout << "Error while parsing JSON in function PUTJson() of HttpHandler: " << error << std::endl; - throw(std::runtime_error("Error while parsing JSON in function PUTJson() of HttpHandler")); - } - return result; -} - -Json::Value HttpHandler::DELETEJson(std::string uri, const Json::Value& body, const std::string &adr, int port) const -{ - std::string response = DELETEString(uri, "application/json", body.toStyledString(), adr, port); - - std::string error; - Json::Value result; - Json::CharReaderBuilder builder; - builder["collectComments"] = false; - std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); - if (!reader->parse(response.c_str(), response.c_str() + response.length(), &result, &error)) - { - std::cout << "Error while parsing JSON in function PUTJson() of HttpHandler: " << error << std::endl; - throw(std::runtime_error("Error while parsing JSON in function PUTJson() of HttpHandler")); - } - return result; -} +#endif diff --git a/hueplusplus/winHttpHandler.cpp b/hueplusplus/winHttpHandler.cpp new file mode 100644 index 0000000..cbfd0d7 --- /dev/null +++ b/hueplusplus/winHttpHandler.cpp @@ -0,0 +1,272 @@ +/** +\file winHttpHandler.cpp +Copyright Notice\n +Copyright (C) 2017 Jan Rogall - developer\n +Copyright (C) 2017 Moritz Wirger - developer\n + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. +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. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +**/ + +#include "include/winHttpHandler.h" + +#ifdef _MSC_VER + +#include +#include +#include +#include +#include + +#pragma comment(lib, "Ws2_32.lib") + +winHttpHandler::winHttpHandler() +{ + // Initialize Winsock + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) + { + std::cerr << "Failed to open socket: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("Failed to open socket")); + } +} + +winHttpHandler::~winHttpHandler() +{ + WSACleanup(); +} + +std::string winHttpHandler::send(const std::string & msg, const std::string & adr, int port) const +{ + struct addrinfo *result = nullptr, *ptr = nullptr, hints; + + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + // Resolve the server address and port + int res = getaddrinfo(adr.c_str(), std::to_string(port).c_str(), &hints, &result); + if (res != 0) + { + std::cerr << "getaddrinfo failed: " << res << std::endl; + throw(std::runtime_error("getaddrinfo failed")); + } + + SOCKET connect_socket = INVALID_SOCKET; + + + // Attempt to connect to the first address returned by + // the call to getaddrinfo + ptr = result; + + // Create a SOCKET for connecting to server + connect_socket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); + + if (connect_socket == INVALID_SOCKET) + { + freeaddrinfo(result); + std::cerr << "Error at socket(): " << WSAGetLastError() << std::endl; + throw(std::runtime_error("Error at socket()")); + } + + // Connect to server. + res = connect(connect_socket, ptr->ai_addr, (int)ptr->ai_addrlen); + if (res == SOCKET_ERROR) + { + closesocket(connect_socket); + connect_socket = INVALID_SOCKET; + } + + // Should really try the next address returned by getaddrinfo + // if the connect call failed + // But for this simple example we just free the resources + // returned by getaddrinfo and print an error message + + freeaddrinfo(result); + + if (connect_socket == INVALID_SOCKET) + { + std::cerr << "Unable to connect to server!" << std::endl; + throw(std::runtime_error("Unable to connect to server!")); + } + + // Send an initial buffer + res = ::send(connect_socket, msg.c_str(), msg.size(), 0); + if (res == SOCKET_ERROR) + { + std::cerr << "send failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("send failed")); + } + + // shutdown the connection for sending since no more data will be sent + // the client can still use the ConnectSocket for receiving data + res = shutdown(connect_socket, SD_SEND); + if (res == SOCKET_ERROR) + { + closesocket(connect_socket); + std::cerr << "shutdown failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("shutdown failed")); + } + + const int recvbuflen = 128; + char recvbuf[recvbuflen]; + + // Receive data until the server closes the connection + std::string response; + int received = 0; + do + { + res = recv(connect_socket, recvbuf, recvbuflen, 0); + if (res > 0) + { + std::cout << "Bytes received: " << res << std::endl; + response.append(recvbuf, res); + } + else if (res == 0) + { + std::cout << "Connection closed " << std::endl; + } + else + { + std::cerr << "recv failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("recv failed")); + } + } while (res > 0); + + return response; +} + +std::vector winHttpHandler::sendMulticast(const std::string & msg, const std::string & adr, int port, int timeout) const +{ + struct addrinfo *result = nullptr, *ptr = nullptr, hints; + SOCKADDR_IN source_sin, dest_sin; + + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_TCP; + + // Resolve the server address and port + int res = getaddrinfo(adr.c_str(), std::to_string(port).c_str(), &hints, &result); + if (res != 0) + { + std::cerr << "sendMulticast: getaddrinfo failed: " << res << std::endl; + throw(std::runtime_error("getaddrinfo failed")); + } + + SOCKET connect_socket = INVALID_SOCKET; + + + // Attempt to connect to the first address returned by + // the call to getaddrinfo + ptr = result; + + // Create a SOCKET for connecting to server + if ((connect_socket = socket(ptr->ai_family, ptr->ai_socktype, 0)) == INVALID_SOCKET) + { + freeaddrinfo(result); + std::cerr << "sendMulticast: Error at socket(): " << WSAGetLastError() << std::endl; + throw(std::runtime_error("Error at socket()")); + } + + // Fill out source socket's address information. + source_sin.sin_family = AF_INET; + source_sin.sin_port = htons(0); + source_sin.sin_addr.s_addr = htonl(INADDR_ANY); + + // Associate the source socket's address with the socket, Sock. + if (bind(connect_socket, (struct sockaddr FAR *) &source_sin, sizeof(source_sin)) == SOCKET_ERROR) + { + closesocket(connect_socket); + std::cerr << "sendMulticast: Binding socket failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("Binding socket failed")); + } + + u_long sock_mode = 1; + ioctlsocket(connect_socket, FIONBIO, &sock_mode); + + BOOL bOptVal = TRUE; + setsockopt(connect_socket, SOL_SOCKET, SO_BROADCAST, (char *)&bOptVal, sizeof(bOptVal)); + + // Set the Time-to-Live of the multicast. + int iOptVal = 64; + if (setsockopt(connect_socket, IPPROTO_IP, IP_MULTICAST_TTL, (char FAR *)&iOptVal, sizeof(int)) == SOCKET_ERROR) + { + closesocket(connect_socket); + std::cerr << "sendMulticast: setsockopt failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("setsockopt failed")); + } + + // Fill out the desination socket's address information. + dest_sin.sin_family = AF_INET; + dest_sin.sin_port = htons(port); + dest_sin.sin_addr.s_addr = inet_addr((const char*)ptr->ai_addr); + + // Send a message to the multicasting address. + if (sendto(connect_socket, msg.c_str(), msg.size(), 0, (struct sockaddr FAR *) &dest_sin, sizeof(dest_sin)) == SOCKET_ERROR) + { + std::cerr << "sendMulticast: sendto failed: " << WSAGetLastError() << std::endl; + closesocket(connect_socket); + throw(std::runtime_error("sendto failed")); + } + + // shutdown the connection for sending since no more data will be sent + // the client can still use the ConnectSocket for receiving data + res = shutdown(connect_socket, SD_SEND); + if (res == SOCKET_ERROR) + { + closesocket(connect_socket); + std::cerr << "sendMulticast: shutdown failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("shutdown failed")); + } + + std::string response; + const int recvbuflen = 2048; + char recvbuf[recvbuflen] = {}; + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + while (std::chrono::steady_clock::now() - start < std::chrono::seconds(timeout)) + { + res = recv(connect_socket, recvbuf, recvbuflen, 0); + if (res > 0) + { + std::cout << "sendMulticast: Bytes received: " << res << std::endl; + response.append(recvbuf, res); + } + else if (res == 0) + { + std::cout << "sendMulticast: Connection closed " << std::endl; + } + else + { + // No exception here due to non blocking socket + //std::cerr << "sendMulticast: recv failed: " << WSAGetLastError() << std::endl; + //throw(std::runtime_error("recv failed")); + } + } + closesocket(connect_socket); // Is this needed? + + // construct return vector + std::vector returnString; + size_t pos = response.find("\r\n\r\n"); + size_t prevpos = 0; + while (pos != std::string::npos) + { + returnString.push_back(response.substr(prevpos, pos - prevpos)); + pos += 4; + prevpos = pos; + pos = response.find("\r\n\r\n", pos); + } + + return returnString; +} + +#endif