From 9e554815917bfd7671363baec1d88950498b45ea Mon Sep 17 00:00:00 2001 From: Nodeduino Date: Sun, 19 Nov 2017 21:46:26 +0100 Subject: [PATCH] Add crude windows compatability, - Rename HttpHandler to linHttpHandler - Add new winHttpHandler for windows - Export duplicate functions from win/lin-HttpHandler into IHttpHandler --- hueplusplus/HttpHandler.cpp | 334 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- hueplusplus/include/HttpHandler.h | 158 -------------------------------------------------------------------------------------------------------------------------------------------------------------- hueplusplus/include/IHttpHandler.h | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- hueplusplus/include/linHttpHandler.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ hueplusplus/include/winHttpHandler.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hueplusplus/linHttpHandler.cpp | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hueplusplus/winHttpHandler.cpp | 272 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 716 insertions(+), 502 deletions(-) delete mode 100755 hueplusplus/HttpHandler.cpp delete mode 100755 hueplusplus/include/HttpHandler.h create mode 100644 hueplusplus/include/linHttpHandler.h create mode 100644 hueplusplus/include/winHttpHandler.h create mode 100644 hueplusplus/linHttpHandler.cpp create mode 100644 hueplusplus/winHttpHandler.cpp diff --git a/hueplusplus/HttpHandler.cpp b/hueplusplus/HttpHandler.cpp deleted file mode 100755 index 0ccf199..0000000 --- a/hueplusplus/HttpHandler.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/** - \file HttpHandler.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/HttpHandler.h" - -#include -#include // struct sockaddr_in, struct sockaddr -#include -#include -#include -#include // struct hostent, gethostbyname -#include // socket, connect -#include -#include // printf, sprintf -#include // exit -#include // functions for C style null-terminated strings -#include // read, write, close - -class SocketCloser { - public: SocketCloser(int sockFd) :s(sockFd) {} - ~SocketCloser() { close(s); } - private: int s; -}; - -std::string HttpHandler::send(const std::string & msg, const std::string & adr, int port) const -{ - // create socket - int socketFD = socket(AF_INET, SOCK_STREAM, 0); - - SocketCloser closeMySocket(socketFD); - if (socketFD < 0) - { - std::cerr << "Failed to open socket\n"; - throw(std::runtime_error("Failed to open socket")); - } - - // lookup ip address - hostent *server; - server = gethostbyname(adr.c_str()); - if (server == NULL) - { - std::cerr << "Failed to find host with address " << adr << "\n"; - throw(std::runtime_error("Failed to find host")); - } - - // fill in the structure - sockaddr_in server_addr; - memset(&server_addr, 0, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(port); - memcpy(&server_addr.sin_addr.s_addr, server->h_addr, server->h_length); - - // connect the socket - if (connect(socketFD, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) - { - std::cerr << "Failed to connect socket\n"; - throw(std::runtime_error("Failed to connect socket")); - } - - // send the request - size_t total = msg.length(); - ssize_t sent = 0; - do - { - ssize_t bytes = write(socketFD, msg.c_str() + sent, total - sent); - if (bytes < 0) - { - std::cerr << "Failed to write message to socket\n"; - throw(std::runtime_error("Failed to write message to socket")); - } - if (bytes == 0) - { - break; - } - if (bytes) - { - sent += bytes; - } - } while (sent < total); - - // receive the response - std::string response; - total = sizeof(response) - 1; - int received = 0; - char buffer[128] = {}; - do - { - ssize_t bytes = read(socketFD, buffer, 127); - if (bytes < 0) - { - std::cerr << "Failed to read response from socket: " << errno << std::endl; - throw(std::runtime_error("Failed to read response from socket")); - } - if (bytes == 0) - { - break; - } - if (bytes) - { - received += bytes; - response.append(buffer, bytes); - } - } while (true); - - if (received == total) - { - std::cerr << "Failed to store complete response from socket\n"; - throw(std::runtime_error("Failed to store complete response from socket")); - } - - 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 -{ - hostent *server; // host information - sockaddr_in server_addr; // server address - - //fill in the server's address and data - memset((char*)&server_addr, 0, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(port); - - // look up the address of the server given its name - server = gethostbyname(adr.c_str()); - if (!server) - { - std::cerr << "Failed to obtain address of " << msg << "\n"; - throw(std::runtime_error("Failed to obtain address of host")); - } - - // put the host's address into the server address structure - memcpy( (void *)&server_addr.sin_addr, server->h_addr_list[0], server->h_length ); - - // create the socket - int socketFD = socket(AF_INET, SOCK_DGRAM, 0); - SocketCloser closeMySendSocket(socketFD); - if (socketFD < 0) - { - std::cerr << "Failed to open socket\n"; - throw(std::runtime_error("Failed to open socket")); - } - - // send a message to the server - if (sendto(socketFD, msg.c_str(), strlen(msg.c_str()), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) - { - std::cerr << "Failed to send message\n"; - throw(std::runtime_error("Failed to send message")); - } - - std::string response; - char buffer[2048] = {}; // receive buffer - ssize_t bytesReceived = 0; - - std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); - while (std::chrono::steady_clock::now() - start < std::chrono::seconds(timeout)) - { - bytesReceived = recv(socketFD, &buffer, 2048, MSG_DONTWAIT); - if (bytesReceived < 0) - { - if (errno != EAGAIN && errno != EWOULDBLOCK) - { - std::cerr << "Failed to read response from socket\n"; - throw(std::runtime_error("Failed to read response from socket")); - } - continue; - } - if (bytesReceived == 0) - { - break; - } - if (bytesReceived) - { - response.append(buffer, bytesReceived); - } - } - - // 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; -} - -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; -} 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/linHttpHandler.cpp b/hueplusplus/linHttpHandler.cpp new file mode 100644 index 0000000..90b6460 --- /dev/null +++ b/hueplusplus/linHttpHandler.cpp @@ -0,0 +1,211 @@ +/** + \file linHttpHandler.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/linHttpHandler.h" + +#ifndef _MSC_VER + +#include +#include // struct sockaddr_in, struct sockaddr +#include +#include +#include +#include // struct hostent, gethostbyname +#include // socket, connect +#include +#include // printf, sprintf +#include // exit +#include // functions for C style null-terminated strings +#include // read, write, close + +class SocketCloser { + public: SocketCloser(int sockFd) :s(sockFd) {} + ~SocketCloser() { close(s); } + private: int s; +}; + +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); + + SocketCloser closeMySocket(socketFD); + if (socketFD < 0) + { + std::cerr << "Failed to open socket\n"; + throw(std::runtime_error("Failed to open socket")); + } + + // lookup ip address + hostent *server; + server = gethostbyname(adr.c_str()); + if (server == NULL) + { + std::cerr << "Failed to find host with address " << adr << "\n"; + throw(std::runtime_error("Failed to find host")); + } + + // fill in the structure + sockaddr_in server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(port); + memcpy(&server_addr.sin_addr.s_addr, server->h_addr, server->h_length); + + // connect the socket + if (connect(socketFD, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) + { + std::cerr << "Failed to connect socket\n"; + throw(std::runtime_error("Failed to connect socket")); + } + + // send the request + size_t total = msg.length(); + ssize_t sent = 0; + do + { + ssize_t bytes = write(socketFD, msg.c_str() + sent, total - sent); + if (bytes < 0) + { + std::cerr << "Failed to write message to socket\n"; + throw(std::runtime_error("Failed to write message to socket")); + } + if (bytes == 0) + { + break; + } + if (bytes) + { + sent += bytes; + } + } while (sent < total); + + // receive the response + std::string response; + total = sizeof(response) - 1; + int received = 0; + char buffer[128] = {}; + do + { + ssize_t bytes = read(socketFD, buffer, 127); + if (bytes < 0) + { + std::cerr << "Failed to read response from socket: " << errno << std::endl; + throw(std::runtime_error("Failed to read response from socket")); + } + if (bytes == 0) + { + break; + } + if (bytes) + { + received += bytes; + response.append(buffer, bytes); + } + } while (true); + + if (received == total) + { + std::cerr << "Failed to store complete response from socket\n"; + throw(std::runtime_error("Failed to store complete response from socket")); + } + + return response; +} + +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 + + //fill in the server's address and data + memset((char*)&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(port); + + // look up the address of the server given its name + server = gethostbyname(adr.c_str()); + if (!server) + { + std::cerr << "Failed to obtain address of " << msg << "\n"; + throw(std::runtime_error("Failed to obtain address of host")); + } + + // put the host's address into the server address structure + memcpy( (void *)&server_addr.sin_addr, server->h_addr_list[0], server->h_length ); + + // create the socket + int socketFD = socket(AF_INET, SOCK_DGRAM, 0); + SocketCloser closeMySendSocket(socketFD); + if (socketFD < 0) + { + std::cerr << "Failed to open socket\n"; + throw(std::runtime_error("Failed to open socket")); + } + + // send a message to the server + if (sendto(socketFD, msg.c_str(), strlen(msg.c_str()), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) + { + std::cerr << "Failed to send message\n"; + throw(std::runtime_error("Failed to send message")); + } + + std::string response; + char buffer[2048] = {}; // receive buffer + ssize_t bytesReceived = 0; + + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + while (std::chrono::steady_clock::now() - start < std::chrono::seconds(timeout)) + { + bytesReceived = recv(socketFD, &buffer, 2048, MSG_DONTWAIT); + if (bytesReceived < 0) + { + if (errno != EAGAIN && errno != EWOULDBLOCK) + { + std::cerr << "Failed to read response from socket\n"; + throw(std::runtime_error("Failed to read response from socket")); + } + continue; + } + if (bytesReceived == 0) + { + break; + } + if (bytesReceived) + { + response.append(buffer, bytesReceived); + } + } + + // 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 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 -- libgit2 0.21.4