diff --git a/toREST/include/http.hpp b/toREST/include/http.hpp index b346e81..21b7681 100644 --- a/toREST/include/http.hpp +++ b/toREST/include/http.hpp @@ -1,56 +1,58 @@ -#ifndef _TOREST_HTTP_HPP_ -#define _TOREST_HTTP_HPP_ +#ifndef _TR_HTTP_HPP_ +#define _TR_HTTP_HPP_ #include #include namespace http { - /// http codes - enum status { - /*! Standard response for successful HTTP requests. */ ok=200, - /*! The request has been fulfilled, resulting in the creation of a new resource. */ created=201, - /*! The request has been accepted for processing, but the processing has not been completed. */ accepted=202, - /*! The server successfully processed the request and is not returning any content. */ no_content=204, - /*! The server cannot or will not process the request due to an apparent client error */ bad_request=400, - /*! Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. */ unauthorized=401, - /*! Reserved for future use. The */ payment_required=402, - /*! the request was a valid request, but the server is refusing to respond to it. */ forbidden=403, - /*! The requested resource could not be found but may be available in the future. */ not_found=404, - /*! Allowed A request method is not supported for the requested resource */ method_not_allowed=405, - /*! The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request. */ not_acceptable=406, - /*! A generic error message, given when an unexpected condition was encountered and no more specific message is suitable. */ internal_server_error=500, - /*! The server either does not recognize the request method, or it lacks the ability to fulfill the request. */ not_implemented=501, - /*! The server was acting as a gateway or proxy and received an invalid response from the upstream server. */ bad_gateway=502, - /*! The server is currently unavailable (because it is overloaded or down for maintenance). Generally, this is a temporary state. */ service_unavailable=503, - /*! Gateway timed out */ gateway_timeout=504, - /*! http-version not supported */ http_version_not_supported=505 - }; - typedef std::pair header; - typedef std::unordered_map headers; - - /// creates a http::code object out of a http::status code - class code { - public: - code(status status); - public: - status first; - std::string second; - }; +/// http codes +enum status { + /*! Standard response for successful HTTP requests. */ ok = 200, + /*! The request has been fulfilled, resulting in the creation of a new resource. */ created = 201, + /*! The request has been accepted for processing, but the processing has not been completed. */ accepted = 202, + /*! The server successfully processed the request and is not returning any content. */ no_content = 204, + /*! The server cannot or will not process the request due to an apparent client error */ bad_request = 400, + /*! Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. */ unauthorized = 401, + /*! Reserved for future use. The */ payment_required = 402, + /*! the request was a valid request, but the server is refusing to respond to it. */ forbidden = 403, + /*! The requested resource could not be found but may be available in the future. */ not_found = 404, + /*! Allowed A request method is not supported for the requested resource */ method_not_allowed = 405, + /*! The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request. */ not_acceptable = 406, + /*! A generic error message, given when an unexpected condition was encountered and no more specific message is suitable. */ internal_server_error = 500, + /*! The server either does not recognize the request method, or it lacks the ability to fulfill the request. */ not_implemented = 501, + /*! The server was acting as a gateway or proxy and received an invalid response from the upstream server. */ bad_gateway = 502, + /*! The server is currently unavailable (because it is overloaded or down for maintenance). Generally, this is a temporary state. */ service_unavailable = 503, + /*! Gateway timed out */ gateway_timeout = 504, + /*! http-version not supported */ http_version_not_supported = 505 +}; +typedef std::pair header; +typedef std::unordered_map headers; + +/// creates a http::code object out of a http::status code +class code { +public: + code(status status); + +public: + status first; + std::string second; +}; + +class response { +public: + friend std::ostream &operator<<(std::ostream &os, const response &rh) { return rh.do_response(os); } + response() : status_code(http::ok) {} + void set_body(const std::string &content) { body = content; } + void set_body(const nlohmann::json &json); + void add_header(const http::header &header) { response_headers.emplace(header); } + void set_status(http::status code) { status_code = code; } + std::ostream &do_response(std::ostream &os) const; - class response { - public: - friend std::ostream& operator<<(std::ostream& os,const response &rh){ return rh.do_response(os); } - response():status_code(http::ok){} - void set_body(const std::string &content) { body=content; } - void set_body(const nlohmann::json &json); - void add_header(const http::header &header) { response_headers.emplace(header); } - void set_status(http::status code) { status_code=code; } - std::ostream& do_response(std::ostream &os) const; - protected: - std::string body; - headers response_headers; - code status_code; - }; +protected: + std::string body; + headers response_headers; + code status_code; +}; }; -#endif // _TOREST_HTTP_HPP_ +#endif diff --git a/toREST/include/session.hpp b/toREST/include/session.hpp index e60fd6c..649f64c 100644 --- a/toREST/include/session.hpp +++ b/toREST/include/session.hpp @@ -1,70 +1,75 @@ +#ifndef _TR_SESSION_HPP_ +#define _TR_SESSION_HPP_ + #include #include -namespace tr{ - namespace session { - template - void get(torrent_session&session,response resp,request req){ - auto http_response = http::response(); - if (session.is_valid()) { - const auto settings = session.get_settings(); - http_response.set_body({ - { "paused", session.is_paused() }, - { "port", session.listen_port() }, - { "dht_enabled", session.is_dht_running() }, - { "down_limit", settings.get_int(settings.download_rate_limit) }, - { "up_limit", settings.get_int(settings.upload_rate_limit) }, - { "torrents", session.get_torrents().size() }, //TODO Optimize -// { "default_download_dir", default_download_dir.filename().string() }, -// { "root_dir", root_dir.string() }, - }); - } else { - auto response_code = http::code(http::service_unavailable); - http_response.set_body({{"code", response_code.first}, { "status", response_code.second}}); - http_response.set_status(response_code.first); - } - *resp << http_response; - } - template - void patch(torrent_session&session,response resp,request req){ - auto http_response = http::response(); - if (!session.is_valid()) { - auto response_code = http::code(http::service_unavailable); - http_response.set_body({{"code",response_code.first},{"status", response_code.second}}); - http_response.set_status(response_code.first); - } else { - const auto json=util::json::parse(req->content); - if(json.is_object() && !json.is_null()) { - if(util::json::get("paused",json,session.is_paused())) - session.pause(); - else - session.resume(); - auto settings = session.get_settings(); - const auto new_listen_port=util::json::get("port",json,session.listen_port()); - if(new_listen_port!=session.listen_port()) - settings.set_str(settings.listen_interfaces,"0.0.0.0:"+std::to_string(new_listen_port)); - const auto new_dht_enabled=util::json::get("dht_enabled",json,session.is_dht_running()); - if(new_dht_enabled!=session.is_dht_running()) - settings.set_bool(settings.enable_dht,new_dht_enabled); - auto old_limit=settings.get_int(settings.download_rate_limit); - auto new_limit=util::json::get("down_limit",json,old_limit); - if(new_limit!=old_limit) - settings.set_int(settings.download_rate_limit,new_limit); - old_limit=settings.get_int(settings.upload_rate_limit); - new_limit=util::json::get("up_limit",json,old_limit); - if(new_limit!=old_limit) - settings.set_int(settings.upload_rate_limit,new_limit); - session.apply_settings(settings); - auto response_code = http::code(http::accepted); - http_response.set_body({{"code",response_code.first},{"status", response_code.second}}); - http_response.set_status(response_code.first); - } else { - auto response_code = http::code(http::bad_request); - http_response.set_body({{"code",response_code.first},{"status", response_code.second}}); - http_response.set_status(response_code.first); - } - } - *resp << http_response; +namespace tr { +namespace session { +template +void get(torrent_session &session, response resp, request req) { + auto http_response = http::response(); + if (session.is_valid()) { + const auto settings = session.get_settings(); + http_response.set_body({ + {"paused", session.is_paused()}, + {"port", session.listen_port()}, + {"dht_enabled", session.is_dht_running()}, + {"down_limit", settings.get_int(settings.download_rate_limit)}, + {"up_limit", settings.get_int(settings.upload_rate_limit)}, + {"torrents", session.get_torrents().size()}, //TODO Optimize + // { "default_download_dir", default_download_dir.filename().string() }, + // { "root_dir", root_dir.string() }, + }); + } else { + auto response_code = http::code(http::service_unavailable); + http_response.set_body({{"code", response_code.first}, {"status", response_code.second}}); + http_response.set_status(response_code.first); + } + *resp << http_response; +} +template +void patch(torrent_session &session, response resp, request req) { + auto http_response = http::response(); + if (!session.is_valid()) { + auto response_code = http::code(http::service_unavailable); + http_response.set_body({{"code", response_code.first}, {"status", response_code.second}}); + http_response.set_status(response_code.first); + } else { + const auto json = util::json::parse(req->content); + if (json.is_object() && !json.is_null()) { + if (util::json::get("paused", json, session.is_paused())) + session.pause(); + else + session.resume(); + auto settings = session.get_settings(); + const auto new_listen_port = util::json::get("port", json, session.listen_port()); + if (new_listen_port != session.listen_port()) + settings.set_str(settings.listen_interfaces, "0.0.0.0:" + std::to_string(new_listen_port)); + const auto new_dht_enabled = util::json::get("dht_enabled", json, session.is_dht_running()); + if (new_dht_enabled != session.is_dht_running()) + settings.set_bool(settings.enable_dht, new_dht_enabled); + auto old_limit = settings.get_int(settings.download_rate_limit); + auto new_limit = util::json::get("down_limit", json, old_limit); + if (new_limit != old_limit) + settings.set_int(settings.download_rate_limit, new_limit); + old_limit = settings.get_int(settings.upload_rate_limit); + new_limit = util::json::get("up_limit", json, old_limit); + if (new_limit != old_limit) + settings.set_int(settings.upload_rate_limit, new_limit); + session.apply_settings(settings); + auto response_code = http::code(http::accepted); + http_response.set_body({{"code", response_code.first}, {"status", response_code.second}}); + http_response.set_status(response_code.first); + } else { + auto response_code = http::code(http::bad_request); + http_response.set_body({{"code", response_code.first}, {"status", response_code.second}}); + http_response.set_status(response_code.first); } } -} \ No newline at end of file + *resp << http_response; +} +} +} + +#endif \ No newline at end of file diff --git a/toREST/include/util.hpp b/toREST/include/util.hpp index fb24b8e..23c6d05 100644 --- a/toREST/include/util.hpp +++ b/toREST/include/util.hpp @@ -1,84 +1,82 @@ -#ifndef _BT_HELPERS_HPP_ -#define _BT_HELPERS_HPP_ +#ifndef _TR_UTIL_HPP_ +#define _TR_UTIL_HPP_ -#include #include #include +#include namespace util { - class uri { - public: - auto static parse(const std::string &request_path){ - std::unordered_map options; - std::string option, value; - bool collect=false; - for(auto &c:request_path){ - switch(c){ - case '&': - options.insert(std::make_pair(option,value)); - option=""; - collect=true; - break; - case '?': - collect=true; - break; - case '=': - collect=false; - value=""; - break; - default: - if(collect) - option+=c; - else - value+=c; - break; - } +class uri { +public: + auto static parse(const std::string &request_path) { + std::unordered_map options; + std::string option, value; + bool collect = false; + for (auto &c : request_path) { + switch (c) { + case '&': + options.insert(std::make_pair(option, value)); + option = ""; + collect = true; + break; + case '?': + collect = true; + break; + case '=': + collect = false; + value = ""; + break; + default: + if (collect) + option += c; + else + value += c; + break; } - if(!option.empty() || !value.empty()) - options.insert(std::make_pair(option,value)); - return options; } - }; - const boost::regex regex("@(https?|ftp)://(-\\.)?([^\\s/?\\.#-]+\\.?)+(/[^\\s]*)?$@", boost::regex_constants::perl | boost::regex_constants::icase); + if (!option.empty() || !value.empty()) + options.insert(std::make_pair(option, value)); + return options; + } +}; +const boost::regex regex("@(https?|ftp)://(-\\.)?([^\\s/?\\.#-]+\\.?)+(/[^\\s]*)?$@", boost::regex_constants::perl | boost::regex_constants::icase); - class json { - public: - /*! @brief Wrapper for nlohmann::json::parse. If the parse fails, result.is_null() will be */ - static nlohmann::json parse(std::istream &istream) { - try { - return nlohmann::json::parse(istream); - } catch(const std::invalid_argument &exp) { - - } - return nlohmann::json(nullptr); +class json { +public: + /*! @brief Wrapper for nlohmann::json::parse. If the parse fails, result.is_null() will be */ + static nlohmann::json parse(std::istream &istream) { + try { + return nlohmann::json::parse(istream); + } catch (const std::invalid_argument &exp) { } - /*! @brief wrapper around nlohmann::json::parse, but object returns null on throw */ - static nlohmann::json parse(const std::string &string){ - try { - return nlohmann::json::parse(string); - } catch(const std::invalid_argument &exp) { - - } - return nlohmann::json(nullptr); + return nlohmann::json(nullptr); + } + /*! @brief wrapper around nlohmann::json::parse, but object returns null on throw */ + static nlohmann::json parse(const std::string &string) { + try { + return nlohmann::json::parse(string); + } catch (const std::invalid_argument &exp) { } - static bool has_property(const std::string &key, const nlohmann::json &object){ - auto it=object.find(key); - return it!=object.end(); - } - template - static auto get(const std::string &key, const nlohmann::json &object, const T &default_value){ - if(has_property(key,object)){ - T r; - try{ - r=object[key]; //TODO fix use json parser where a non-throwable exist - } catch(const std::exception&){ - return default_value; - } - return r; + return nlohmann::json(nullptr); + } + static bool has_property(const std::string &key, const nlohmann::json &object) { + auto it = object.find(key); + return it != object.end(); + } + template + static auto get(const std::string &key, const nlohmann::json &object, const T &default_value) { + if (has_property(key, object)) { + T r; + try { + r = object[key]; //TODO fix use json parser where a non-throwable exist + } catch (const std::exception &) { + return default_value; } - return default_value; + return r; } - }; + return default_value; + } +}; } -#endif // _BT_HELPERS_HPP_ +#endif diff --git a/toREST/src/http.cpp b/toREST/src/http.cpp index 28c9ef4..00dfa83 100644 --- a/toREST/src/http.cpp +++ b/toREST/src/http.cpp @@ -1,40 +1,17 @@ #include -http::code::code(status status):first(status){ - switch(status) { - case accepted: second="Accepted"; break; - case bad_gateway: second="Bad Gateway"; break; - case bad_request: second="Bad Request"; break; - case created: second="Created"; break; - case forbidden: second="Forbidden"; break; - case gateway_timeout: second="Gateway Timeout"; break; - case http_version_not_supported: second="HTTP Version Not Supported"; break; - case internal_server_error: second="Internal Server Error"; break; - case method_not_allowed: second="Method Not Allowed"; break; - case not_acceptable: second="Not Acceptable"; break; - case no_content: second="No Content"; break; - case not_found: second="Not Found"; break; - case not_implemented: second="Not Implemented"; break; - case ok: second="OK"; break; - case payment_required: second="Payment Required"; break; - case service_unavailable: second="Service Unavailable"; break; - case unauthorized: second="Unauthorized"; break; - default: second="UNKNOWN"; - } -} - std::ostream &http::response::do_response(std::ostream &os) const { os << "HTTP/1.1" << " " << status_code.first << " " << status_code.second << "\r\n"; - for(auto &header : response_headers) + for (auto &header : response_headers) os << header.first << ": " << header.second << "\r\n"; - if(status_code.first != http::no_content) { + if (status_code.first != http::no_content) { os << "Content-Length: "; - if(!body.empty()) { + if (!body.empty()) { return os << body.size() << "\r\n\r\n" << body; } else { - const auto body_replace=std::to_string(status_code.first) + " " + status_code.second; + const auto body_replace = std::to_string(status_code.first) + " " + status_code.second; return os << body_replace.size() << "\r\n\r\n" << body_replace; } @@ -42,7 +19,65 @@ std::ostream &http::response::do_response(std::ostream &os) const { return os << "\r\n"; } -void http::response::set_body(const nlohmann::json &json){ - add_header({"Content-Type","application/json"}); +void http::response::set_body(const nlohmann::json &json) { + add_header({"Content-Type", "application/json"}); response::set_body(json.dump()); } + +http::code::code(status status) : first(status) { + switch (status) { + case accepted: + second = "Accepted"; + break; + case bad_gateway: + second = "Bad Gateway"; + break; + case bad_request: + second = "Bad Request"; + break; + case created: + second = "Created"; + break; + case forbidden: + second = "Forbidden"; + break; + case gateway_timeout: + second = "Gateway Timeout"; + break; + case http_version_not_supported: + second = "HTTP Version Not Supported"; + break; + case internal_server_error: + second = "Internal Server Error"; + break; + case method_not_allowed: + second = "Method Not Allowed"; + break; + case not_acceptable: + second = "Not Acceptable"; + break; + case no_content: + second = "No Content"; + break; + case not_found: + second = "Not Found"; + break; + case not_implemented: + second = "Not Implemented"; + break; + case ok: + second = "OK"; + break; + case payment_required: + second = "Payment Required"; + break; + case service_unavailable: + second = "Service Unavailable"; + break; + case unauthorized: + second = "Unauthorized"; + break; + default: + second = "UNKNOWN"; + } +} \ No newline at end of file diff --git a/toREST/tests/include/ConfigContext.hpp b/toREST/tests/include/ConfigContext.hpp index 02e8385..ffd9cb3 100644 --- a/toREST/tests/include/ConfigContext.hpp +++ b/toREST/tests/include/ConfigContext.hpp @@ -3,5 +3,4 @@ #include - #endif \ No newline at end of file diff --git a/toREST/tests/include/ServerContext.hpp b/toREST/tests/include/ServerContext.hpp index ea83740..d3e26ac 100644 --- a/toREST/tests/include/ServerContext.hpp +++ b/toREST/tests/include/ServerContext.hpp @@ -1,9 +1,10 @@ #ifndef _TR_TEST_SERVER_CONTEXT_HPP_ #define _TR_TEST_SERVER_CONTEXT_HPP_ + #include #include -#include #include +#include class TestRequest { public: @@ -13,12 +14,12 @@ public: class TestResponse : public std::stringstream { public: std::string buffer; - std::unordered_map headers_; + std::unordered_map headers_; std::string string(); std::string message(); std::string code(); std::string content(); - const std::unordered_map& headers(); + const std::unordered_map &headers(); }; class CommonResponse { diff --git a/toREST/tests/include/TorrentContext.hpp b/toREST/tests/include/TorrentContext.hpp index e62c2f5..083ebbe 100644 --- a/toREST/tests/include/TorrentContext.hpp +++ b/toREST/tests/include/TorrentContext.hpp @@ -1,8 +1,8 @@ #ifndef _TR_TEST_TORRENT_CONTEXT_HPP_ #define _TR_TEST_TORRENT_CONTEXT_HPP_ -#include #include +#include class TorrentStatus { public: diff --git a/toREST/tests/torrents_test.cpp b/toREST/tests/torrents_test.cpp index a749f8e..f3d3de8 100644 --- a/toREST/tests/torrents_test.cpp +++ b/toREST/tests/torrents_test.cpp @@ -141,7 +141,7 @@ SCENARIO("We are running a POST /session/torrents resource") { request->content << nlohmann::json::object( {{"magnet_uri", magnet_uri}, {"save_path", "music"}, - {"up_speed", 90}, + {"up_speed", 90}, {"down_speed", "100"}, {"name", name}}); THEN("the server should reply with bad request") {