diff --git a/hueplusplus/HttpHandler.cpp b/hueplusplus/HttpHandler.cpp old mode 100755 new mode 100644 index d3e3b82..31dc916 --- a/hueplusplus/HttpHandler.cpp +++ b/hueplusplus/HttpHandler.cpp @@ -19,6 +19,7 @@ #include "include/HttpHandler.h" #include +#include #include #include // printf, sprintf #include // exit @@ -36,7 +37,7 @@ class SocketCloser { private: int s; }; -std::string HttpHandler::sendRequest(const std::string & msg, const std::string & adr, int port) +std::string HttpHandler::send(const std::string & msg, const std::string & adr, int port) { // create socket int socketFD = socket(AF_INET, SOCK_STREAM, 0); @@ -125,9 +126,9 @@ std::string HttpHandler::sendRequest(const std::string & msg, const std::string return response; } -std::string HttpHandler::sendRequestGetBody(const std::string & msg, const std::string & adr, int port) +std::string HttpHandler::sendGetHTTPBody(const std::string & msg, const std::string & adr, int port) { - std::string response = sendRequest(msg, adr, port); + std::string response = send(msg, adr, port); size_t start = response.find("\r\n\r\n"); if (start == std::string::npos) { @@ -216,3 +217,95 @@ std::vector HttpHandler::sendMulticast(const std::string & msg, con return returnString; } + +std::string HttpHandler::sendHTTPRequest(std::string method, std::string uri, std::string content_type, std::string body, const std::string &adr, int port) +{ + 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) +{ + 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) +{ + 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) +{ + return sendHTTPRequest("PUT", uri, content_type, body, adr, port); +} + +Json::Value HttpHandler::GETJson(std::string uri, const Json::Value& body, const std::string &adr, int port) +{ + 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 SendRequest() of HueLight: " << error << std::endl; + throw(std::runtime_error("Error while parsing JSON in function SendRequest() of HueLight")); + } + return result; +} + +Json::Value HttpHandler::POSTJson(std::string uri, const Json::Value& body, const std::string &adr, int port) +{ + 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 SendRequest() of HueLight: " << error << std::endl; + throw(std::runtime_error("Error while parsing JSON in function SendRequest() of HueLight")); + } + return result; +} + +Json::Value HttpHandler::PUTJson(std::string uri, const Json::Value& body, const std::string &adr, int port) +{ + 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 SendRequest() of HueLight: " << error << std::endl; + throw(std::runtime_error("Error while parsing JSON in function SendRequest() of HueLight")); + } + return result; +} diff --git a/hueplusplus/Hue.cpp b/hueplusplus/Hue.cpp old mode 100755 new mode 100644 index 0286260..f7b0bae --- a/hueplusplus/Hue.cpp +++ b/hueplusplus/Hue.cpp @@ -56,7 +56,7 @@ std::vector HueFinder::FindBridges() const unsigned int start = p.first.find("//") + 2; unsigned int length = p.first.find(":", start) - start; bridge.ip = p.first.substr(start, length); - std::string desc = HttpHandler().sendRequestGetBody("GET /description.xml HTTP/1.0\r\nContent-Type: application/xml\r\nContent-Length: 0\r\n\r\n\r\n\r\n", bridge.ip); + std::string desc = HttpHandler().GETString("/description.xml", "application/xml", "", bridge.ip); std::smatch matchResult; if (std::regex_search(desc, manufRegex) && std::regex_search(desc, manURLRegex) && std::regex_search(desc, modelRegex) && std::regex_search(desc, matchResult, serialRegex)) { @@ -106,29 +106,13 @@ const std::map& HueFinder::GetAllUsernames() const return usernames; } +//! \todo Remove duplicate code found in HueFinder::RequestUsername and Hue::requestUsername std::string HueFinder::RequestUsername(const std::string & ip) const { std::cout << "Please press the link Button! You've got 35 secs!\n"; // when the link button was presed we got 30 seconds to get our username for control Json::Value request; - request["devicetype"] = "HuePlusPlus#System User"; - - // POST /api HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: \r\n\r\n\r\n\r\n - - std::string post; - post.append("POST /api HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: "); - post.append(std::to_string(request.toStyledString().size())); - post.append("\r\n\r\n"); - post.append(request.toStyledString()); - post.append("\r\n\r\n"); - - std::cout << post << std::endl; - std::cout << ip << std::endl; - - Json::CharReaderBuilder builder; - builder["collectComments"] = false; - std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); - std::string error; + request["devicetype"] = "HuePlusPlus#User"; Json::Value answer; std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); @@ -138,12 +122,8 @@ std::string HueFinder::RequestUsername(const std::string & ip) const if (std::chrono::steady_clock::now() - lastCheck > std::chrono::seconds(1)) { lastCheck = std::chrono::steady_clock::now(); - std::string postAnswer = HttpHandler().sendRequestGetBody(post.c_str(), ip, 80); - if (!reader->parse(postAnswer.c_str(), postAnswer.c_str() + postAnswer.length(), &answer, &error)) - { - std::cout << "Error while parsing JSON in getUsername of Hue: " << error << std::endl; - throw(std::runtime_error("Error while parsing JSON in getUsername of Hue")); - } + answer = HttpHandler().GETJson("/api", request, ip); + if (answer[0]["success"] != Json::nullValue) { // [{"success":{"username": "83b7780291a6ceffbe0bd049104df"}}] @@ -161,8 +141,8 @@ std::string HueFinder::RequestUsername(const std::string & ip) const } -Hue::Hue(const std::string& ip, const std::string& username) : -ip(ip), +Hue::Hue(const std::string& ip, const std::string& username) : +ip(ip), username(username) { simpleBrightnessStrategy = std::make_shared(); @@ -180,26 +160,9 @@ std::string Hue::getBridgeIP() void Hue::requestUsername(const std::string& ip) { std::cout << "Please press the link Button! You've got 35 secs!\n"; // when the link button was presed we got 30 seconds to get our username for control - - Json::Value request; - request["devicetype"] = "Enwiro Smarthome#System User"; - - // POST /api HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: \r\n\r\n\r\n\r\n - - std::string post; - post.append("POST /api HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: "); - post.append(std::to_string(request.toStyledString().size())); - post.append("\r\n\r\n"); - post.append(request.toStyledString()); - post.append("\r\n\r\n"); - std::cout << post << std::endl; - std::cout << ip << std::endl; - - Json::CharReaderBuilder builder; - builder["collectComments"] = false; - std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); - std::string error; + Json::Value request; + request["devicetype"] = "HuePlusPlus#User"; Json::Value answer; std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); @@ -209,12 +172,7 @@ void Hue::requestUsername(const std::string& ip) if (std::chrono::steady_clock::now() - lastCheck > std::chrono::seconds(1)) { lastCheck = std::chrono::steady_clock::now(); - std::string postAnswer = HttpHandler().sendRequestGetBody(post.c_str(), ip, 80); - if (!reader->parse(postAnswer.c_str(), postAnswer.c_str() + postAnswer.length(), &answer, &error)) - { - std::cout << "Error while parsing JSON in getUsername of Hue: " << error << std::endl; - throw(std::runtime_error("Error while parsing JSON in getUsername of Hue")); - } + answer = HttpHandler().GETJson("/api", request, ip); if (answer[0]["success"] != Json::nullValue) { // [{"success":{"username": ""}}] @@ -326,30 +284,13 @@ void Hue::refreshState() { return; } - std::string get; - get.append("GET /api/"); - get.append(username); - get.append(" HTTP / 1.0\r\nContent - Type: application / json\r\nContent - Length: 0\r\n\r\n\r\n\r\n"); - - Json::CharReaderBuilder builder; - builder["collectComments"] = false; - std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); - std::string error; - - Json::Value answer; - std::string postAnswer = HttpHandler().sendRequestGetBody(get.c_str(), ip, 80); - //std::cout <<"\""<< postAnswer << "\"\n" << std::endl; - if (!reader->parse(postAnswer.c_str(), postAnswer.c_str() + postAnswer.length(), &answer, &error)) - { - std::cout << "Error while parsing JSON in refreshState of Hue: " << error << std::endl; - throw(std::runtime_error("Error while parsing JSON in refreshState of Hue")); - } + Json::Value answer = HttpHandler().GETJson("/api/"+username, Json::objectValue, ip); if (answer.isObject() && answer.isMember("lights")) { state = answer; } else { - std::cout << "Answer in refreshState() of HttpHandler().sendRequestGetBody() is not expected!\n"; + std::cout << "Answer in refreshState() of HttpHandler().sendGetHTTPBody() is not expected!\n"; } } diff --git a/hueplusplus/HueLight.cpp b/hueplusplus/HueLight.cpp old mode 100755 new mode 100644 index fd91954..c9c102c --- a/hueplusplus/HueLight.cpp +++ b/hueplusplus/HueLight.cpp @@ -78,7 +78,7 @@ bool HueLight::alert() return false; } -HueLight::HueLight(const std::string& ip, const std::string& username, int id) +HueLight::HueLight(const std::string& ip, const std::string& username, int id) : HueLight(ip, username, id, nullptr, nullptr, nullptr) {} @@ -173,54 +173,16 @@ bool HueLight::OffNoRefresh(uint8_t transition) Json::Value HueLight::SendPutRequest(const Json::Value& request) { - std::string put; - put.append("PUT /api/"); - put.append(username); - put.append("/lights/"); - put.append(std::to_string(id)); - put.append("/state HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: "); - put.append(std::to_string(request.toStyledString().size())); - put.append("\r\n\r\n"); - put.append(request.toStyledString()); - put.append("\r\n\r\n"); - - Json::CharReaderBuilder builder; - builder["collectComments"] = false; - std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); - std::string error; - std::string putAnswer = HttpHandler().sendRequestGetBody(put.c_str(), ip, 80); - - Json::Value result; - if (!reader->parse(putAnswer.c_str(), putAnswer.c_str() + putAnswer.length(), &result, &error)) - { - std::cout << "Error while parsing JSON in function SendRequest() of HueLight: " << error << std::endl; - throw(std::runtime_error("Error while parsing JSON in function SendRequest() of HueLight")); - } - return result; + return HttpHandler().PUTJson("/api/"+username+"/lights/"+std::to_string(id)+"/state", request, ip); } void HueLight::refreshState() { std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); std::cout << "\tRefreshing lampstate of lamp with id: " << id << ", ip: " << ip << "\n"; - // GET /api//lights/ HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: \r\n\r\n\r\n\r\n - std::string get; - get.append("GET /api/"); - get.append(username); - get.append("/lights/"); - get.append(std::to_string(id)); - get.append(" HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: 0\r\n\r\n\r\n\r\n"); - - Json::CharReaderBuilder builder; - builder["collectComments"] = false; - std::unique_ptr reader = std::unique_ptr(builder.newCharReader()); - std::string error; - std::string getAnswer = HttpHandler().sendRequestGetBody(get.c_str(), ip, 80); - // todo check whether getAnswer is containing right information - if (!reader->parse(getAnswer.c_str(), getAnswer.c_str() + getAnswer.length(), &state, &error)) - { - std::cout << "Error while parsing JSON in function refreshState() of HueLight: " << error << std::endl; - throw(std::runtime_error("Error while parsing JSON in function refreshState() of HueLight")); - } + + state = HttpHandler().GETJson("/api/"+username+"/lights/"+std::to_string(id), Json::objectValue, ip); + //! \todo check whether getAnswer is containing right information + std::cout << "\tRefresh state took: " << std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count() << "ms" << std::endl; -} \ No newline at end of file +} diff --git a/hueplusplus/include/HttpHandler.h b/hueplusplus/include/HttpHandler.h index fc3f96d..88a16f9 100644 --- a/hueplusplus/include/HttpHandler.h +++ b/hueplusplus/include/HttpHandler.h @@ -23,27 +23,30 @@ #include #include +#include "json/json.h" + //! Class to handle http requests and multicast requests class HttpHandler { public: - //! \brief Function that sends a http request with the specified message and returns the response. + //! \brief Function that sends a given message to the specified host and returns the response. //! //! It returns a string containing the response of the host. - //! \param msg String that contains the request that is sent to the specified address + //! \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 sendRequest(const std::string &msg, const std::string &adr, int port=80); + std::string send(const std::string &msg, const std::string &adr, int port=80); - //! \brief Function that sends a http request with the specified message and returns the body of the response. + //! \brief Function that sends a given message to the specified host and returns the body of the response. //! //! It returns a string containing only the body of the response of the host. - //! \param msg String that contains the request that is sent to the specified address + //! 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 sendRequestGetBody(const std::string &msg, const std::string &adr, int port = 80); + std::string sendGetHTTPBody(const std::string &msg, const std::string &adr, int port = 80); //! \brief Function that sends a multicast request with the specified message. //! @@ -54,6 +57,88 @@ public: //! \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); + + //! \brief Function that sends a HTTP request with the given method to the specified host and returns the body of the response. + //! + //! It returns a string containing only the body of the response of the host. + //! 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); + + //! \brief Function that sends a HTTP GET request to the specified host and returns the body of the response. + //! + //! It returns a string containing only the body of the response of the host. + //! 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); + + //! \brief Function that sends a HTTP POST request to the specified host and returns the body of the response. + //! + //! It returns a string containing only the body of the response of the host. + //! 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); + + //! \brief Function that sends a HTTP PUT request to the specified host and returns the body of the response. + //! + //! It returns a string containing only the body of the response of the host. + //! 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); + + //! \brief Function that sends a HTTP GET request to the specified host and returns the body of the response. + //! + //! It returns a Json::Value parsed from the body of the response of the host. + //! 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); + + //! \brief Function that sends a HTTP POST request to the specified host and returns the body of the response. + //! + //! It returns a Json::Value parsed from the body of the response of the host. + //! 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); + + //! \brief Function that sends a HTTP PUT request to the specified host and returns the body of the response. + //! + //! It returns a Json::Value parsed from the body of the response of the host. + //! 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); }; #endif