From 3fdc280cb2331fd16fd484c1a75e86b8a1abcacc Mon Sep 17 00:00:00 2001 From: Nodeduino Date: Sat, 14 Oct 2017 20:12:28 +0200 Subject: [PATCH] Move HTTP specific implementations out of Hue classes into HttpHandler and provide new functions abstracting said implementations, rename some old HttpHandler functions to match their task and clean up some stuff --- hueplusplus/HttpHandler.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- hueplusplus/Hue.cpp | 83 ++++++++++++----------------------------------------------------------------------- hueplusplus/HueLight.cpp | 52 +++++++--------------------------------------------- hueplusplus/include/HttpHandler.h | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 206 insertions(+), 125 deletions(-) mode change 100755 => 100644 hueplusplus/HttpHandler.cpp mode change 100755 => 100644 hueplusplus/Hue.cpp mode change 100755 => 100644 hueplusplus/HueLight.cpp 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 -- libgit2 0.21.4