From 8a0c7fe4b819b2dd9560585297531e4c7ca94cdb Mon Sep 17 00:00:00 2001 From: Moritz Wirger Date: Fri, 26 Jan 2018 21:26:07 +0100 Subject: [PATCH] Make IHttpHandler purely virtual - add new class BaseHttpHandler - rename lin- and winHttpHandler to Lin- and WinHttpHandler - print error messages to std::cerr --- README.md | 10 +++++----- hueplusplus/LinHttpHandler.cpp | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hueplusplus/WinHttpHandler.cpp | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hueplusplus/include/BaseHttpHandler.h | 241 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hueplusplus/include/IHttpHandler.h | 99 ++++++++++----------------------------------------------------------------------------------------- hueplusplus/include/LinHttpHandler.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ hueplusplus/include/WinHttpHandler.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hueplusplus/include/linHttpHandler.h | 52 ---------------------------------------------------- hueplusplus/include/winHttpHandler.h | 62 -------------------------------------------------------------- hueplusplus/linHttpHandler.cpp | 203 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- hueplusplus/test/CMakeLists.txt | 2 +- hueplusplus/test/mocks/mock_LinHttpHandler.h | 40 ++++++++++++++++++++++++++++++++++++++++ hueplusplus/test/mocks/mock_linHttpHandler.h | 40 ---------------------------------------- hueplusplus/test/test_BaseHttpHandler.cpp | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hueplusplus/test/test_IHttpHandler.cpp | 197 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- hueplusplus/winHttpHandler.cpp | 261 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 16 files changed, 1072 insertions(+), 910 deletions(-) create mode 100755 hueplusplus/LinHttpHandler.cpp create mode 100755 hueplusplus/WinHttpHandler.cpp create mode 100755 hueplusplus/include/BaseHttpHandler.h create mode 100755 hueplusplus/include/LinHttpHandler.h create mode 100755 hueplusplus/include/WinHttpHandler.h delete mode 100755 hueplusplus/include/linHttpHandler.h delete mode 100644 hueplusplus/include/winHttpHandler.h delete mode 100755 hueplusplus/linHttpHandler.cpp create mode 100755 hueplusplus/test/mocks/mock_LinHttpHandler.h delete mode 100755 hueplusplus/test/mocks/mock_linHttpHandler.h create mode 100755 hueplusplus/test/test_BaseHttpHandler.cpp delete mode 100755 hueplusplus/test/test_IHttpHandler.cpp delete mode 100644 hueplusplus/winHttpHandler.cpp diff --git a/README.md b/README.md index 0f57f8a..441fc54 100755 --- a/README.md +++ b/README.md @@ -15,14 +15,14 @@ A simple and easy to use library for Philips Hue Lights ## How to use ### Searching for Bridges -To start searching for a Hue Bridge you will need to choose an IHttpHandler and create one. The options are a "winHttpHandler" (for windows) or a "linHttpHandler" (for linux). +To start searching for a Hue Bridge you will need to choose an IHttpHandler and create one. The options are a "WinHttpHandler" (for windows) or a "LinHttpHandler" (for linux). Then create a HueFinder object with the handler. The handler is needed, because it tells the finder which functions to use to communicate with a bridge or your local network. After that you can call FindBridges(), which will return a vector containing the ip and mac address of all found Bridges. If no Bridges were found the vector is empty, so make sure that in that case you provide an ip and mac address. ```C++ -// For windows use std::make_shared(); -handler = std::make_shared(); +// For windows use std::make_shared(); +handler = std::make_shared(); HueFinder finder(handler); std::vector bridges = finder.FindBridges(); if (bridges.empty()) @@ -46,8 +46,8 @@ Hue bridge = finder.GetBridge(bridges[0]); If you do not want to use the HueFinder or you already know the ip and username of your bridge you have the option to create your own Hue object. Here you will need to provide the ip address, a username and an HttpHandler ```C++ -// For windows use std::make_shared(); -handler = std::make_shared(); +// For windows use std::make_shared(); +handler = std::make_shared(); Hue bridge("192.168.2.102", "", handler); ``` diff --git a/hueplusplus/LinHttpHandler.cpp b/hueplusplus/LinHttpHandler.cpp new file mode 100755 index 0000000..352bef1 --- /dev/null +++ b/hueplusplus/LinHttpHandler.cpp @@ -0,0 +1,203 @@ +/** + \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" + +#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 << "LinHttpHandler: Failed to open socket\n"; + throw(std::runtime_error("LinHttpHandler: Failed to open socket")); + } + + // lookup ip address + hostent *server; + server = gethostbyname(adr.c_str()); + if (server == NULL) + { + std::cerr << "LinHttpHandler: Failed to find host with address " << adr << "\n"; + throw(std::runtime_error("LinHttpHandler: 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 << "LinHttpHandler: Failed to connect socket\n"; + throw(std::runtime_error("LinHttpHandler: 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 << "LinHttpHandler: Failed to write message to socket\n"; + throw(std::runtime_error("LinHttpHandler: 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 << "LinHttpHandler: Failed to read response from socket: " << errno << std::endl; + throw(std::runtime_error("LinHttpHandler: 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 << "LinHttpHandler: Failed to store complete response from socket\n"; + throw(std::runtime_error("LinHttpHandler: 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 << "LinHttpHandler: sendMulticast: Failed to obtain address of " << msg << "\n"; + throw(std::runtime_error("LinHttpHandler: sendMulticast: 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 << "LinHttpHandler: sendMulticast: Failed to open socket\n"; + throw(std::runtime_error("LinHttpHandler: sendMulticast: 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 << "LinHttpHandler: sendMulticast: Failed to send message\n"; + throw(std::runtime_error("LinHttpHandler: sendMulticast: 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 << "LinHttpHandler: sendMulticast: Failed to read response from socket\n"; + throw(std::runtime_error("LinHttpHandler: sendMulticast: Failed to read response from socket")); + } + continue; + } + 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; +} diff --git a/hueplusplus/WinHttpHandler.cpp b/hueplusplus/WinHttpHandler.cpp new file mode 100755 index 0000000..7251dc5 --- /dev/null +++ b/hueplusplus/WinHttpHandler.cpp @@ -0,0 +1,261 @@ +/** +\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" + +#include +#include +#include +#include +#include + +#pragma comment(lib, "Ws2_32.lib") + +WinHttpHandler::WinHttpHandler() +{ + // Initialize Winsock + int return_code = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (return_code != 0) + { + std::cerr << "Failed to open socket: " << return_code << std::endl; + throw(std::runtime_error("WinHttpHandler: Failed to open socket")); + } +} + +WinHttpHandler::~WinHttpHandler() +{ + WSACleanup(); +} + +std::string WinHttpHandler::send(const std::string & msg, const std::string & adr, int port) const +{ + struct addrinfo hints = {}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + // Resolve the server address and port + struct addrinfo *result = nullptr; + if (getaddrinfo(adr.c_str(), std::to_string(port).c_str(), &hints, &result) != 0) + { + std::cerr << "WinHttpHandler: getaddrinfo failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("WinHttpHandler: getaddrinfo failed")); + } + + // Attempt to connect to the first address returned by + // the call to getaddrinfo + struct addrinfo *ptr = result; + + // Create a SOCKET for connecting to server + SOCKET connect_socket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); + + if (connect_socket == INVALID_SOCKET) + { + freeaddrinfo(result); + std::cerr << "WinHttpHandler: Error at socket(): " << WSAGetLastError() << std::endl; + throw(std::runtime_error("WinHttpHandler: Error at socket()")); + } + + // Connect to server. + if (connect(connect_socket, ptr->ai_addr, (int)ptr->ai_addrlen) == 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 << "WinHttpHandler: Unable to connect to server!" << std::endl; + throw(std::runtime_error("WinHttpHandler: Unable to connect to server!")); + } + + // Send an initial buffer + if (::send(connect_socket, msg.c_str(), msg.size(), 0) == SOCKET_ERROR) + { + closesocket(connect_socket); + std::cerr << "WinHttpHandler: send failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("WinHttpHandler: send failed")); + } + + // shutdown the connection for sending since no more data will be sent + // the client can still use the ConnectSocket for receiving data + if (shutdown(connect_socket, SD_SEND) == SOCKET_ERROR) + { + closesocket(connect_socket); + std::cerr << "WinHttpHandler: shutdown failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("WinHttpHandler: shutdown failed")); + } + + const int recvbuflen = 128; + char recvbuf[recvbuflen]; + + // Receive data until the server closes the connection + std::string response; + int res; + do + { + res = recv(connect_socket, recvbuf, recvbuflen, 0); + if (res > 0) + { + //std::cout << "WinHttpHandler: Bytes received: " << res << std::endl; + response.append(recvbuf, res); + } + else if (res == 0) + { + //std::cout << "WinHttpHandler: Connection closed " << std::endl; + } + else + { + closesocket(connect_socket); + std::cerr << "WinHttpHandler: recv failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("WinHttpHandler: recv failed")); + } + } while (res > 0); + closesocket(connect_socket); + + return response; +} + +std::vector WinHttpHandler::sendMulticast(const std::string & msg, const std::string & adr, int port, int timeout) const +{ + struct addrinfo hints = {}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_TCP; + + // Resolve the server address and port + struct addrinfo *result = nullptr; + if (getaddrinfo(adr.c_str(), std::to_string(port).c_str(), &hints, &result) != 0) + { + std::cerr << "WinHttpHandler: sendMulticast: getaddrinfo failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("WinHttpHandler: sendMulticast: getaddrinfo failed")); + } + + // Attempt to connect to the first address returned by + // the call to getaddrinfo + struct addrinfo *ptr = result; + + // Create a SOCKET for connecting to server + SOCKET connect_socket; + if ((connect_socket = socket(ptr->ai_family, ptr->ai_socktype, 0)) == INVALID_SOCKET) + { + freeaddrinfo(result); + std::cerr << "WinHttpHandler: sendMulticast: Error at socket(): " << WSAGetLastError() << std::endl; + throw(std::runtime_error("WinHttpHandler: sendMulticast: Error at socket()")); + } + + // Fill out source socket's address information. + SOCKADDR_IN source_sin; + 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 << "WinHttpHandler: sendMulticast: Binding socket failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("WinHttpHandler: sendMulticast: 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 = 1; // for same subnet, but might be increased to 16 + if (setsockopt(connect_socket, IPPROTO_IP, IP_MULTICAST_TTL, (char FAR *)&iOptVal, sizeof(int)) == SOCKET_ERROR) + { + closesocket(connect_socket); + std::cerr << "WinHttpHandler: sendMulticast: setsockopt failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("WinHttpHandler: sendMulticast: setsockopt failed")); + } + + // Fill out the desination socket's address information. + SOCKADDR_IN dest_sin; + 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) + { + closesocket(connect_socket); + std::cerr << "WinHttpHandler: sendMulticast: sendto failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("WinHttpHandler: sendMulticast: sendto failed")); + } + + // shutdown the connection for sending since no more data will be sent + // the client can still use the ConnectSocket for receiving data + if (shutdown(connect_socket, SD_SEND) == SOCKET_ERROR) + { + closesocket(connect_socket); + std::cerr << "WinHttpHandler: sendMulticast: shutdown failed: " << WSAGetLastError() << std::endl; + throw(std::runtime_error("WinHttpHandler: sendMulticast: shutdown failed")); + } + + std::string response; + const int recvbuflen = 2048; + char recvbuf[recvbuflen] = {}; + int res; + 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 << "WinHttpHandler: sendMulticast: Bytes received: " << res << std::endl; + response.append(recvbuf, res); + } + else if (res == 0) + { + //std::cout << "WinHttpHandler: 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; +} diff --git a/hueplusplus/include/BaseHttpHandler.h b/hueplusplus/include/BaseHttpHandler.h new file mode 100755 index 0000000..0b14da8 --- /dev/null +++ b/hueplusplus/include/BaseHttpHandler.h @@ -0,0 +1,241 @@ +/** + \file BaseHttpHandler.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 _BASE_HTTPHANDLER_H +#define _BASE_HTTPHANDLER_H + +#include +#include +#include +#include + +#include "IHttpHandler.h" +#include "json/json.h" + +//! Base class for classes that handle http requests and multicast requests +class BaseHttpHandler : public IHttpHandler +{ +public: + //! \brief Virtual dtor + virtual ~BaseHttpHandler() = default; + + //! \brief Virtual function that should send a given message to a specified host and return the response. + //! + //! \param msg String that contains the message that should be 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 should be sent to. Default is 80 + //! \return String containing the response of the host + virtual std::string send(const std::string &msg, const std::string &adr, int port=80) const = 0; + + //! \brief Virtual function that should given message to a specified host and return the body of the response. + //! + //! Note if no body is found a runtime error is thrown! + //! \param msg String that contains the message that should 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 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 + { + std::string response = send(msg, adr, port); + size_t start = response.find("\r\n\r\n"); + if (start == std::string::npos) + { + std::cerr << "IHttpHandler: Failed to find body in response\n"; + throw(std::runtime_error("IHttpHandler: 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. + //! + //! \param msg String that contains the request that should be 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 should be 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 + virtual std::vector sendMulticast(const std::string &msg, const std::string &adr = "239.255.255.250", int port = 1900, int timeout = 5) const = 0; + + //! \brief Virtual function that should send a HTTP request with the given method to the specified host and return 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 + 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. + //! + //! 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 + 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. + //! + //! 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 + 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. + //! + //! 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 + 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. + //! + //! 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 + 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. + //! + //! 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 + virtual Json::Value GETJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const + { + return strToJsonValue(GETString(uri, "application/json", body.toStyledString(), adr, port)); + }; + + //! \brief Virtual function that should send a HTTP POST request to the specified host and return 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 + virtual Json::Value POSTJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const + { + return strToJsonValue(POSTString(uri, "application/json", body.toStyledString(), adr, port)); + } + + //! \brief Virtual function that should send a HTTP PUT request to the specified host and return 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 + virtual Json::Value PUTJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const + { + return strToJsonValue(PUTString(uri, "application/json", body.toStyledString(), adr, port)); + }; + + //! \brief Virtual function that should send a HTTP DELETE request to the specified host and return 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 + virtual Json::Value DELETEJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const + { + return strToJsonValue(DELETEString(uri, "application/json", body.toStyledString(), adr, port)); + }; + +private: + + //! \brief Function that converts a given string to a Json::Value + //! + //! \param str String that gets converted + //! \return Json::Value containing parsed string + Json::Value strToJsonValue(std::string str) const + { + std::string error; + Json::Value result; + Json::CharReaderBuilder builder; + builder["collectComments"] = false; + std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); + if (!reader->parse(str.c_str(), str.c_str() + str.length(), &result, &error)) + { + std::cerr << "IHttpHandler: Error while parsing JSON in function strToJsonValue(): " << error << std::endl; + throw(std::runtime_error("IHttpHandler: Error while parsing JSON in function strToJsonValue()")); + } + return result; + } +}; + +#endif diff --git a/hueplusplus/include/IHttpHandler.h b/hueplusplus/include/IHttpHandler.h index b6ea4f4..78a3631 100755 --- a/hueplusplus/include/IHttpHandler.h +++ b/hueplusplus/include/IHttpHandler.h @@ -49,18 +49,7 @@ 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 - { - std::string response = send(msg, adr, port); - size_t start = response.find("\r\n\r\n"); - if (start == std::string::npos) - { - std::cerr << "IHttpHandler: Failed to find body in response\n"; - throw(std::runtime_error("IHttpHandler: Failed to find body in response")); - } - response.erase(0, start + 4); - return response; - }; + virtual std::string sendGetHTTPBody(const std::string &msg, const std::string &adr, int port = 80) const = 0; //! \brief Virtual function that should send a multicast request with a specified message. //! @@ -81,31 +70,7 @@ 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 - { - 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); - }; + 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; //! \brief Virtual function that should send a HTTP GET request to the specified host and return the body of the response. //! @@ -116,10 +81,7 @@ 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 - { - return sendHTTPRequest("GET", uri, content_type, body, adr, port); - }; + virtual std::string GETString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const = 0; //! \brief Virtual function that should send a HTTP POST request to the specified host and returns the body of the response. //! @@ -130,10 +92,7 @@ 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 - { - return sendHTTPRequest("POST", uri, content_type, body, adr, port); - }; + virtual std::string POSTString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const = 0; //! \brief Virtual function that should send a HTTP PUT request to the specified host and return the body of the response. //! @@ -144,10 +103,7 @@ 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 - { - return sendHTTPRequest("PUT", uri, content_type, body, adr, port); - }; + virtual std::string PUTString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const = 0; //! \brief Virtual function that should send a HTTP DELETE request to the specified host and return the body of the response. //! @@ -158,10 +114,7 @@ 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 - { - return sendHTTPRequest("DELETE", uri, content_type, body, adr, port); - }; + virtual std::string DELETEString(std::string uri, std::string content_type, std::string body, const std::string &adr, int port=80) const = 0; //! \brief Virtual function that should send a HTTP GET request to the specified host and return the body of the response. //! @@ -171,10 +124,7 @@ 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 - { - return strToJsonValue(GETString(uri, "application/json", body.toStyledString(), adr, port)); - }; + virtual Json::Value GETJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const = 0; //! \brief Virtual function that should send a HTTP POST request to the specified host and return the body of the response. //! @@ -184,10 +134,7 @@ 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 - { - return strToJsonValue(POSTString(uri, "application/json", body.toStyledString(), adr, port)); - } + virtual Json::Value POSTJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const = 0; //! \brief Virtual function that should send a HTTP PUT request to the specified host and return the body of the response. //! @@ -197,10 +144,7 @@ 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 - { - return strToJsonValue(PUTString(uri, "application/json", body.toStyledString(), adr, port)); - }; + virtual Json::Value PUTJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const = 0; //! \brief Virtual function that should send a HTTP DELETE request to the specified host and return the body of the response. //! @@ -210,31 +154,8 @@ 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 - { - return strToJsonValue(DELETEString(uri, "application/json", body.toStyledString(), adr, port)); - }; + virtual Json::Value DELETEJson(std::string uri, const Json::Value& body, const std::string &adr, int port=80) const = 0; -private: - - //! \brief Function that converts a given string to a Json::Value - //! - //! \param str String that gets converted - //! \return Json::Value containing parsed string - Json::Value strToJsonValue(std::string str) const - { - std::string error; - Json::Value result; - Json::CharReaderBuilder builder; - builder["collectComments"] = false; - std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); - if (!reader->parse(str.c_str(), str.c_str() + str.length(), &result, &error)) - { - std::cout << "IHttpHandler: Error while parsing JSON in function strToJsonValue(): " << error << std::endl; - throw(std::runtime_error("IHttpHandler: Error while parsing JSON in function strToJsonValue()")); - } - return result; - } }; #endif diff --git a/hueplusplus/include/LinHttpHandler.h b/hueplusplus/include/LinHttpHandler.h new file mode 100755 index 0000000..41a2329 --- /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 "BaseHttpHandler.h" + +#include "json/json.h" + +//! Class to handle http requests and multicast requests on linux systems +class LinHttpHandler : public BaseHttpHandler +{ +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 + virtual 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 + virtual 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 100755 index 0000000..7f07845 --- /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 "BaseHttpHandler.h" + +#include "json/json.h" + +//! Class to handle http requests and multicast requests on windows systems +class WinHttpHandler : public BaseHttpHandler +{ +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/include/linHttpHandler.h b/hueplusplus/include/linHttpHandler.h deleted file mode 100755 index 76f4104..0000000 --- a/hueplusplus/include/linHttpHandler.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - \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 - virtual 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 - virtual 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 deleted file mode 100644 index 56642ef..0000000 --- a/hueplusplus/include/winHttpHandler.h +++ /dev/null @@ -1,62 +0,0 @@ -/** -\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 deleted file mode 100755 index 4b35a84..0000000 --- a/hueplusplus/linHttpHandler.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/** - \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" - -#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 << "linHttpHandler: Failed to open socket\n"; - throw(std::runtime_error("linHttpHandler: Failed to open socket")); - } - - // lookup ip address - hostent *server; - server = gethostbyname(adr.c_str()); - if (server == NULL) - { - std::cerr << "linHttpHandler: Failed to find host with address " << adr << "\n"; - throw(std::runtime_error("linHttpHandler: 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 << "linHttpHandler: Failed to connect socket\n"; - throw(std::runtime_error("linHttpHandler: 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 << "linHttpHandler: Failed to write message to socket\n"; - throw(std::runtime_error("linHttpHandler: 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 << "linHttpHandler: Failed to read response from socket: " << errno << std::endl; - throw(std::runtime_error("linHttpHandler: 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 << "linHttpHandler: Failed to store complete response from socket\n"; - throw(std::runtime_error("linHttpHandler: 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 << "linHttpHandler: sendMulticast: Failed to obtain address of " << msg << "\n"; - throw(std::runtime_error("linHttpHandler: sendMulticast: 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 << "linHttpHandler: sendMulticast: Failed to open socket\n"; - throw(std::runtime_error("linHttpHandler: sendMulticast: 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 << "linHttpHandler: sendMulticast: Failed to send message\n"; - throw(std::runtime_error("linHttpHandler: sendMulticast: 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 << "linHttpHandler: sendMulticast: Failed to read response from socket\n"; - throw(std::runtime_error("linHttpHandler: sendMulticast: Failed to read response from socket")); - } - continue; - } - 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; -} diff --git a/hueplusplus/test/CMakeLists.txt b/hueplusplus/test/CMakeLists.txt index b0816d2..0691793 100755 --- a/hueplusplus/test/CMakeLists.txt +++ b/hueplusplus/test/CMakeLists.txt @@ -34,11 +34,11 @@ endif() # define all test sources set(TEST_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/test_BaseHttpHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_ExtendedColorHueStrategy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_ExtendedColorTemperatureStrategy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_Hue.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_HueLight.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/test_IHttpHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_Main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_SimpleBrightnessStrategy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_SimpleColorHueStrategy.cpp diff --git a/hueplusplus/test/mocks/mock_LinHttpHandler.h b/hueplusplus/test/mocks/mock_LinHttpHandler.h new file mode 100755 index 0000000..574fb3d --- /dev/null +++ b/hueplusplus/test/mocks/mock_LinHttpHandler.h @@ -0,0 +1,40 @@ +/** + \file mock_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 _MOCK_LIN_HTTPHANDLER_H +#define _MOCK_LIN_HTTPHANDLER_H + +#include +#include + +#include + +#include "../hueplusplus/include/LinHttpHandler.h" +#include "../hueplusplus/include/json/json.h" + +//! Mock Class +class MockLinHttpHandler : public LinHttpHandler +{ +public: + MOCK_CONST_METHOD3( send, std::string(const std::string &msg, const std::string &adr, int port) ); + + MOCK_CONST_METHOD4( sendMulticast, std::vector(const std::string &msg, const std::string &adr, int port, int timeout) ); +}; + +#endif diff --git a/hueplusplus/test/mocks/mock_linHttpHandler.h b/hueplusplus/test/mocks/mock_linHttpHandler.h deleted file mode 100755 index 1fe2746..0000000 --- a/hueplusplus/test/mocks/mock_linHttpHandler.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - \file mock_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 _MOCK_LIN_HTTPHANDLER_H -#define _MOCK_LIN_HTTPHANDLER_H - -#include -#include - -#include - -#include "../hueplusplus/include/linHttpHandler.h" -#include "../hueplusplus/include/json/json.h" - -//! Mock Class -class MockLinHttpHandler : public linHttpHandler -{ -public: - MOCK_CONST_METHOD3( send, std::string(const std::string &msg, const std::string &adr, int port) ); - - MOCK_CONST_METHOD4( sendMulticast, std::vector(const std::string &msg, const std::string &adr, int port, int timeout) ); -}; - -#endif diff --git a/hueplusplus/test/test_BaseHttpHandler.cpp b/hueplusplus/test/test_BaseHttpHandler.cpp new file mode 100755 index 0000000..e2660b1 --- /dev/null +++ b/hueplusplus/test/test_BaseHttpHandler.cpp @@ -0,0 +1,197 @@ +#include +#include + +#include "../include/json/json.h" +#include "mocks/mock_LinHttpHandler.h" +#include "testhelper.h" + +#include +#include + +TEST(BaseHttpHandler, sendGetHTTPBody) +{ + using namespace ::testing; + MockLinHttpHandler handler; + + EXPECT_CALL(handler, send("testmsg", "192.168.2.1", 90)) + .Times(AtLeast(2)) + .WillOnce(Return("")) + .WillRepeatedly(Return("\r\n\r\ntestreply")); + + EXPECT_THROW(handler.sendGetHTTPBody("testmsg", "192.168.2.1", 90), std::runtime_error); + EXPECT_EQ("testreply", handler.sendGetHTTPBody("testmsg", "192.168.2.1", 90)); +} + +TEST(BaseHttpHandler, sendHTTPRequest) +{ + using namespace ::testing; + MockLinHttpHandler handler; + + EXPECT_CALL(handler, send("GET UrI HTTP/1.0\r\nContent-Type: text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n", "192.168.2.1", 90)) + .Times(AtLeast(2)) + .WillOnce(Return("")) + .WillRepeatedly(Return("\r\n\r\ntestreply")); + + EXPECT_THROW(handler.sendHTTPRequest("GET", "UrI", "text/html", "body", "192.168.2.1", 90), std::runtime_error); + EXPECT_EQ("testreply", handler.sendHTTPRequest("GET", "UrI", "text/html", "body", "192.168.2.1", 90)); +} + +TEST(BaseHttpHandler, GETString) +{ + using namespace ::testing; + MockLinHttpHandler handler; + + EXPECT_CALL(handler, send("GET UrI HTTP/1.0\r\nContent-Type: text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n", "192.168.2.1", 90)) + .Times(AtLeast(2)) + .WillOnce(Return("")) + .WillRepeatedly(Return("\r\n\r\ntestreply")); + + EXPECT_THROW(handler.GETString("UrI", "text/html", "body", "192.168.2.1", 90), std::runtime_error); + EXPECT_EQ("testreply", handler.GETString("UrI", "text/html", "body", "192.168.2.1", 90)); +} + +TEST(BaseHttpHandler, POSTString) +{ + using namespace ::testing; + MockLinHttpHandler handler; + + EXPECT_CALL(handler, send("POST UrI HTTP/1.0\r\nContent-Type: text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n", "192.168.2.1", 90)) + .Times(AtLeast(2)) + .WillOnce(Return("")) + .WillRepeatedly(Return("\r\n\r\ntestreply")); + + EXPECT_THROW(handler.POSTString("UrI", "text/html", "body", "192.168.2.1", 90), std::runtime_error); + EXPECT_EQ("testreply", handler.POSTString("UrI", "text/html", "body", "192.168.2.1", 90)); +} + +TEST(BaseHttpHandler, PUTString) +{ + using namespace ::testing; + MockLinHttpHandler handler; + + EXPECT_CALL(handler, send("PUT UrI HTTP/1.0\r\nContent-Type: text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n", "192.168.2.1", 90)) + .Times(AtLeast(2)) + .WillOnce(Return("")) + .WillRepeatedly(Return("\r\n\r\ntestreply")); + + EXPECT_THROW(handler.PUTString("UrI", "text/html", "body", "192.168.2.1", 90), std::runtime_error); + EXPECT_EQ("testreply", handler.PUTString("UrI", "text/html", "body", "192.168.2.1", 90)); +} + +TEST(BaseHttpHandler, DELETEString) +{ + using namespace ::testing; + MockLinHttpHandler handler; + + EXPECT_CALL(handler, send("DELETE UrI HTTP/1.0\r\nContent-Type: text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n", "192.168.2.1", 90)) + .Times(AtLeast(2)) + .WillOnce(Return("")) + .WillRepeatedly(Return("\r\n\r\ntestreply")); + + EXPECT_THROW(handler.DELETEString("UrI", "text/html", "body", "192.168.2.1", 90), std::runtime_error); + EXPECT_EQ("testreply", handler.DELETEString("UrI", "text/html", "body", "192.168.2.1", 90)); +} + +TEST(BaseHttpHandler, GETJson) +{ + using namespace ::testing; + MockLinHttpHandler handler; + + Json::Value testval; + testval["test"] = 100; + std::string expected_call = "GET UrI HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: "; + expected_call.append(std::to_string(testval.toStyledString().size())); + expected_call.append("\r\n\r\n"); + expected_call.append(testval.toStyledString()); + expected_call.append("\r\n\r\n"); + + EXPECT_CALL(handler, send(expected_call, "192.168.2.1", 90)) + .Times(AtLeast(2)) + .WillOnce(Return("")) + .WillOnce(Return("\r\n\r\n")) + .WillRepeatedly(Return("\r\n\r\n{\"test\" : \"whatever\"}")); + Json::Value expected; + expected["test"] = "whatever"; + + EXPECT_THROW(handler.GETJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); + EXPECT_THROW(handler.GETJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); + EXPECT_EQ(expected, handler.GETJson("UrI", testval, "192.168.2.1", 90)); +} + +TEST(BaseHttpHandler, POSTJson) +{ + using namespace ::testing; + MockLinHttpHandler handler; + + Json::Value testval; + testval["test"] = 100; + std::string expected_call = "POST UrI HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: "; + expected_call.append(std::to_string(testval.toStyledString().size())); + expected_call.append("\r\n\r\n"); + expected_call.append(testval.toStyledString()); + expected_call.append("\r\n\r\n"); + + EXPECT_CALL(handler, send(expected_call, "192.168.2.1", 90)) + .Times(AtLeast(2)) + .WillOnce(Return("")) + .WillOnce(Return("\r\n\r\n")) + .WillRepeatedly(Return("\r\n\r\n{\"test\" : \"whatever\"}")); + Json::Value expected; + expected["test"] = "whatever"; + + EXPECT_THROW(handler.POSTJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); + EXPECT_THROW(handler.POSTJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); + EXPECT_EQ(expected, handler.POSTJson("UrI", testval, "192.168.2.1", 90)); +} + +TEST(BaseHttpHandler, PUTJson) +{ + using namespace ::testing; + MockLinHttpHandler handler; + + Json::Value testval; + testval["test"] = 100; + std::string expected_call = "PUT UrI HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: "; + expected_call.append(std::to_string(testval.toStyledString().size())); + expected_call.append("\r\n\r\n"); + expected_call.append(testval.toStyledString()); + expected_call.append("\r\n\r\n"); + + EXPECT_CALL(handler, send(expected_call, "192.168.2.1", 90)) + .Times(AtLeast(2)) + .WillOnce(Return("")) + .WillOnce(Return("\r\n\r\n")) + .WillRepeatedly(Return("\r\n\r\n{\"test\" : \"whatever\"}")); + Json::Value expected; + expected["test"] = "whatever"; + + EXPECT_THROW(handler.PUTJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); + EXPECT_THROW(handler.PUTJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); + EXPECT_EQ(expected, handler.PUTJson("UrI", testval, "192.168.2.1", 90)); +} + +TEST(BaseHttpHandler, DELETEJson) +{ + using namespace ::testing; + MockLinHttpHandler handler; + + Json::Value testval; + testval["test"] = 100; + std::string expected_call = "DELETE UrI HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: "; + expected_call.append(std::to_string(testval.toStyledString().size())); + expected_call.append("\r\n\r\n"); + expected_call.append(testval.toStyledString()); + expected_call.append("\r\n\r\n"); + + EXPECT_CALL(handler, send(expected_call, "192.168.2.1", 90)) + .Times(AtLeast(2)) + .WillOnce(Return("")) + .WillOnce(Return("\r\n\r\n")) + .WillRepeatedly(Return("\r\n\r\n{\"test\" : \"whatever\"}")); + Json::Value expected; + expected["test"] = "whatever"; + + EXPECT_THROW(handler.DELETEJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); + EXPECT_THROW(handler.DELETEJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); + EXPECT_EQ(expected, handler.DELETEJson("UrI", testval, "192.168.2.1", 90)); +} diff --git a/hueplusplus/test/test_IHttpHandler.cpp b/hueplusplus/test/test_IHttpHandler.cpp deleted file mode 100755 index bf2826d..0000000 --- a/hueplusplus/test/test_IHttpHandler.cpp +++ /dev/null @@ -1,197 +0,0 @@ -#include -#include - -#include "../include/json/json.h" -#include "mocks/mock_linHttpHandler.h" -#include "testhelper.h" - -#include -#include - -TEST(IHttpHandler, sendGetHTTPBody) -{ - using namespace ::testing; - MockLinHttpHandler handler; - - EXPECT_CALL(handler, send("testmsg", "192.168.2.1", 90)) - .Times(AtLeast(2)) - .WillOnce(Return("")) - .WillRepeatedly(Return("\r\n\r\ntestreply")); - - EXPECT_THROW(handler.sendGetHTTPBody("testmsg", "192.168.2.1", 90), std::runtime_error); - EXPECT_EQ("testreply", handler.sendGetHTTPBody("testmsg", "192.168.2.1", 90)); -} - -TEST(IHttpHandler, sendHTTPRequest) -{ - using namespace ::testing; - MockLinHttpHandler handler; - - EXPECT_CALL(handler, send("GET UrI HTTP/1.0\r\nContent-Type: text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n", "192.168.2.1", 90)) - .Times(AtLeast(2)) - .WillOnce(Return("")) - .WillRepeatedly(Return("\r\n\r\ntestreply")); - - EXPECT_THROW(handler.sendHTTPRequest("GET", "UrI", "text/html", "body", "192.168.2.1", 90), std::runtime_error); - EXPECT_EQ("testreply", handler.sendHTTPRequest("GET", "UrI", "text/html", "body", "192.168.2.1", 90)); -} - -TEST(IHttpHandler, GETString) -{ - using namespace ::testing; - MockLinHttpHandler handler; - - EXPECT_CALL(handler, send("GET UrI HTTP/1.0\r\nContent-Type: text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n", "192.168.2.1", 90)) - .Times(AtLeast(2)) - .WillOnce(Return("")) - .WillRepeatedly(Return("\r\n\r\ntestreply")); - - EXPECT_THROW(handler.GETString("UrI", "text/html", "body", "192.168.2.1", 90), std::runtime_error); - EXPECT_EQ("testreply", handler.GETString("UrI", "text/html", "body", "192.168.2.1", 90)); -} - -TEST(IHttpHandler, POSTString) -{ - using namespace ::testing; - MockLinHttpHandler handler; - - EXPECT_CALL(handler, send("POST UrI HTTP/1.0\r\nContent-Type: text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n", "192.168.2.1", 90)) - .Times(AtLeast(2)) - .WillOnce(Return("")) - .WillRepeatedly(Return("\r\n\r\ntestreply")); - - EXPECT_THROW(handler.POSTString("UrI", "text/html", "body", "192.168.2.1", 90), std::runtime_error); - EXPECT_EQ("testreply", handler.POSTString("UrI", "text/html", "body", "192.168.2.1", 90)); -} - -TEST(IHttpHandler, PUTString) -{ - using namespace ::testing; - MockLinHttpHandler handler; - - EXPECT_CALL(handler, send("PUT UrI HTTP/1.0\r\nContent-Type: text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n", "192.168.2.1", 90)) - .Times(AtLeast(2)) - .WillOnce(Return("")) - .WillRepeatedly(Return("\r\n\r\ntestreply")); - - EXPECT_THROW(handler.PUTString("UrI", "text/html", "body", "192.168.2.1", 90), std::runtime_error); - EXPECT_EQ("testreply", handler.PUTString("UrI", "text/html", "body", "192.168.2.1", 90)); -} - -TEST(IHttpHandler, DELETEString) -{ - using namespace ::testing; - MockLinHttpHandler handler; - - EXPECT_CALL(handler, send("DELETE UrI HTTP/1.0\r\nContent-Type: text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n", "192.168.2.1", 90)) - .Times(AtLeast(2)) - .WillOnce(Return("")) - .WillRepeatedly(Return("\r\n\r\ntestreply")); - - EXPECT_THROW(handler.DELETEString("UrI", "text/html", "body", "192.168.2.1", 90), std::runtime_error); - EXPECT_EQ("testreply", handler.DELETEString("UrI", "text/html", "body", "192.168.2.1", 90)); -} - -TEST(IHttpHandler, GETJson) -{ - using namespace ::testing; - MockLinHttpHandler handler; - - Json::Value testval; - testval["test"] = 100; - std::string expected_call = "GET UrI HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: "; - expected_call.append(std::to_string(testval.toStyledString().size())); - expected_call.append("\r\n\r\n"); - expected_call.append(testval.toStyledString()); - expected_call.append("\r\n\r\n"); - - EXPECT_CALL(handler, send(expected_call, "192.168.2.1", 90)) - .Times(AtLeast(2)) - .WillOnce(Return("")) - .WillOnce(Return("\r\n\r\n")) - .WillRepeatedly(Return("\r\n\r\n{\"test\" : \"whatever\"}")); - Json::Value expected; - expected["test"] = "whatever"; - - EXPECT_THROW(handler.GETJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); - EXPECT_THROW(handler.GETJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); - EXPECT_EQ(expected, handler.GETJson("UrI", testval, "192.168.2.1", 90)); -} - -TEST(IHttpHandler, POSTJson) -{ - using namespace ::testing; - MockLinHttpHandler handler; - - Json::Value testval; - testval["test"] = 100; - std::string expected_call = "POST UrI HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: "; - expected_call.append(std::to_string(testval.toStyledString().size())); - expected_call.append("\r\n\r\n"); - expected_call.append(testval.toStyledString()); - expected_call.append("\r\n\r\n"); - - EXPECT_CALL(handler, send(expected_call, "192.168.2.1", 90)) - .Times(AtLeast(2)) - .WillOnce(Return("")) - .WillOnce(Return("\r\n\r\n")) - .WillRepeatedly(Return("\r\n\r\n{\"test\" : \"whatever\"}")); - Json::Value expected; - expected["test"] = "whatever"; - - EXPECT_THROW(handler.POSTJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); - EXPECT_THROW(handler.POSTJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); - EXPECT_EQ(expected, handler.POSTJson("UrI", testval, "192.168.2.1", 90)); -} - -TEST(IHttpHandler, PUTJson) -{ - using namespace ::testing; - MockLinHttpHandler handler; - - Json::Value testval; - testval["test"] = 100; - std::string expected_call = "PUT UrI HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: "; - expected_call.append(std::to_string(testval.toStyledString().size())); - expected_call.append("\r\n\r\n"); - expected_call.append(testval.toStyledString()); - expected_call.append("\r\n\r\n"); - - EXPECT_CALL(handler, send(expected_call, "192.168.2.1", 90)) - .Times(AtLeast(2)) - .WillOnce(Return("")) - .WillOnce(Return("\r\n\r\n")) - .WillRepeatedly(Return("\r\n\r\n{\"test\" : \"whatever\"}")); - Json::Value expected; - expected["test"] = "whatever"; - - EXPECT_THROW(handler.PUTJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); - EXPECT_THROW(handler.PUTJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); - EXPECT_EQ(expected, handler.PUTJson("UrI", testval, "192.168.2.1", 90)); -} - -TEST(IHttpHandler, DELETEJson) -{ - using namespace ::testing; - MockLinHttpHandler handler; - - Json::Value testval; - testval["test"] = 100; - std::string expected_call = "DELETE UrI HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: "; - expected_call.append(std::to_string(testval.toStyledString().size())); - expected_call.append("\r\n\r\n"); - expected_call.append(testval.toStyledString()); - expected_call.append("\r\n\r\n"); - - EXPECT_CALL(handler, send(expected_call, "192.168.2.1", 90)) - .Times(AtLeast(2)) - .WillOnce(Return("")) - .WillOnce(Return("\r\n\r\n")) - .WillRepeatedly(Return("\r\n\r\n{\"test\" : \"whatever\"}")); - Json::Value expected; - expected["test"] = "whatever"; - - EXPECT_THROW(handler.DELETEJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); - EXPECT_THROW(handler.DELETEJson("UrI", testval, "192.168.2.1", 90), std::runtime_error); - EXPECT_EQ(expected, handler.DELETEJson("UrI", testval, "192.168.2.1", 90)); -} diff --git a/hueplusplus/winHttpHandler.cpp b/hueplusplus/winHttpHandler.cpp deleted file mode 100644 index 861fd44..0000000 --- a/hueplusplus/winHttpHandler.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/** -\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" - -#include -#include -#include -#include -#include - -#pragma comment(lib, "Ws2_32.lib") - -winHttpHandler::winHttpHandler() -{ - // Initialize Winsock - int return_code = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (return_code != 0) - { - std::cerr << "Failed to open socket: " << return_code << std::endl; - throw(std::runtime_error("winHttpHandler: Failed to open socket")); - } -} - -winHttpHandler::~winHttpHandler() -{ - WSACleanup(); -} - -std::string winHttpHandler::send(const std::string & msg, const std::string & adr, int port) const -{ - struct addrinfo hints = {}; - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - // Resolve the server address and port - struct addrinfo *result = nullptr; - if (getaddrinfo(adr.c_str(), std::to_string(port).c_str(), &hints, &result) != 0) - { - std::cerr << "winHttpHandler: getaddrinfo failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("winHttpHandler: getaddrinfo failed")); - } - - // Attempt to connect to the first address returned by - // the call to getaddrinfo - struct addrinfo *ptr = result; - - // Create a SOCKET for connecting to server - SOCKET connect_socket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); - - if (connect_socket == INVALID_SOCKET) - { - freeaddrinfo(result); - std::cerr << "winHttpHandler: Error at socket(): " << WSAGetLastError() << std::endl; - throw(std::runtime_error("winHttpHandler: Error at socket()")); - } - - // Connect to server. - if (connect(connect_socket, ptr->ai_addr, (int)ptr->ai_addrlen) == 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 << "winHttpHandler: Unable to connect to server!" << std::endl; - throw(std::runtime_error("winHttpHandler: Unable to connect to server!")); - } - - // Send an initial buffer - if (::send(connect_socket, msg.c_str(), msg.size(), 0) == SOCKET_ERROR) - { - closesocket(connect_socket); - std::cerr << "winHttpHandler: send failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("winHttpHandler: send failed")); - } - - // shutdown the connection for sending since no more data will be sent - // the client can still use the ConnectSocket for receiving data - if (shutdown(connect_socket, SD_SEND) == SOCKET_ERROR) - { - closesocket(connect_socket); - std::cerr << "winHttpHandler: shutdown failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("winHttpHandler: shutdown failed")); - } - - const int recvbuflen = 128; - char recvbuf[recvbuflen]; - - // Receive data until the server closes the connection - std::string response; - int res; - do - { - res = recv(connect_socket, recvbuf, recvbuflen, 0); - if (res > 0) - { - //std::cout << "winHttpHandler: Bytes received: " << res << std::endl; - response.append(recvbuf, res); - } - else if (res == 0) - { - //std::cout << "winHttpHandler: Connection closed " << std::endl; - } - else - { - closesocket(connect_socket); - std::cerr << "winHttpHandler: recv failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("winHttpHandler: recv failed")); - } - } while (res > 0); - closesocket(connect_socket); - - return response; -} - -std::vector winHttpHandler::sendMulticast(const std::string & msg, const std::string & adr, int port, int timeout) const -{ - struct addrinfo hints = {}; - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_TCP; - - // Resolve the server address and port - struct addrinfo *result = nullptr; - if (getaddrinfo(adr.c_str(), std::to_string(port).c_str(), &hints, &result) != 0) - { - std::cerr << "winHttpHandler: sendMulticast: getaddrinfo failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("winHttpHandler: sendMulticast: getaddrinfo failed")); - } - - // Attempt to connect to the first address returned by - // the call to getaddrinfo - struct addrinfo *ptr = result; - - // Create a SOCKET for connecting to server - SOCKET connect_socket; - if ((connect_socket = socket(ptr->ai_family, ptr->ai_socktype, 0)) == INVALID_SOCKET) - { - freeaddrinfo(result); - std::cerr << "winHttpHandler: sendMulticast: Error at socket(): " << WSAGetLastError() << std::endl; - throw(std::runtime_error("winHttpHandler: sendMulticast: Error at socket()")); - } - - // Fill out source socket's address information. - SOCKADDR_IN source_sin; - 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 << "winHttpHandler: sendMulticast: Binding socket failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("winHttpHandler: sendMulticast: 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 = 1; // for same subnet, but might be increased to 16 - if (setsockopt(connect_socket, IPPROTO_IP, IP_MULTICAST_TTL, (char FAR *)&iOptVal, sizeof(int)) == SOCKET_ERROR) - { - closesocket(connect_socket); - std::cerr << "winHttpHandler: sendMulticast: setsockopt failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("winHttpHandler: sendMulticast: setsockopt failed")); - } - - // Fill out the desination socket's address information. - SOCKADDR_IN dest_sin; - 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) - { - closesocket(connect_socket); - std::cerr << "winHttpHandler: sendMulticast: sendto failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("winHttpHandler: sendMulticast: sendto failed")); - } - - // shutdown the connection for sending since no more data will be sent - // the client can still use the ConnectSocket for receiving data - if (shutdown(connect_socket, SD_SEND) == SOCKET_ERROR) - { - closesocket(connect_socket); - std::cerr << "winHttpHandler: sendMulticast: shutdown failed: " << WSAGetLastError() << std::endl; - throw(std::runtime_error("winHttpHandler: sendMulticast: shutdown failed")); - } - - std::string response; - const int recvbuflen = 2048; - char recvbuf[recvbuflen] = {}; - int res; - 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 << "winHttpHandler: sendMulticast: Bytes received: " << res << std::endl; - response.append(recvbuf, res); - } - else if (res == 0) - { - //std::cout << "winHttpHandler: 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; -} -- libgit2 0.21.4