8 changed files with 258 additions and 218 deletions
@ -1,56 +1,58 @@ |
|||||||
#ifndef _TOREST_HTTP_HPP_ |
#ifndef _TR_HTTP_HPP_ |
||||||
#define _TOREST_HTTP_HPP_ |
#define _TR_HTTP_HPP_ |
||||||
|
|
||||||
#include <json.hpp> |
#include <json.hpp> |
||||||
#include <unordered_map> |
#include <unordered_map> |
||||||
|
|
||||||
namespace http { |
namespace http { |
||||||
/// http codes
|
/// http codes
|
||||||
enum status { |
enum status { |
||||||
/*! Standard response for successful HTTP requests. */ ok=200, |
/*! 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 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 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 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, |
/*! 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, |
/*! 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, |
/*! 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 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, |
/*! 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, |
/*! 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, |
/*! 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, |
/*! 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 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 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, |
/*! 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, |
/*! Gateway timed out */ gateway_timeout = 504, |
||||||
/*! http-version not supported */ http_version_not_supported=505 |
/*! http-version not supported */ http_version_not_supported = 505 |
||||||
}; |
}; |
||||||
typedef std::pair<std::string,std::string> header; |
typedef std::pair<std::string, std::string> header; |
||||||
typedef std::unordered_map<std::string,std::string> headers; |
typedef std::unordered_map<std::string, std::string> headers; |
||||||
|
|
||||||
/// creates a http::code object out of a http::status code
|
/// creates a http::code object out of a http::status code
|
||||||
class code { |
class code { |
||||||
public: |
public: |
||||||
code(status status); |
code(status status); |
||||||
public: |
|
||||||
|
public: |
||||||
status first; |
status first; |
||||||
std::string second; |
std::string second; |
||||||
}; |
}; |
||||||
|
|
||||||
class response { |
class response { |
||||||
public: |
public: |
||||||
friend std::ostream& operator<<(std::ostream& os,const response &rh){ return rh.do_response(os); } |
friend std::ostream &operator<<(std::ostream &os, const response &rh) { return rh.do_response(os); } |
||||||
response():status_code(http::ok){} |
response() : status_code(http::ok) {} |
||||||
void set_body(const std::string &content) { body=content; } |
void set_body(const std::string &content) { body = content; } |
||||||
void set_body(const nlohmann::json &json); |
void set_body(const nlohmann::json &json); |
||||||
void add_header(const http::header &header) { response_headers.emplace(header); } |
void add_header(const http::header &header) { response_headers.emplace(header); } |
||||||
void set_status(http::status code) { status_code=code; } |
void set_status(http::status code) { status_code = code; } |
||||||
std::ostream& do_response(std::ostream &os) const; |
std::ostream &do_response(std::ostream &os) const; |
||||||
protected: |
|
||||||
|
protected: |
||||||
std::string body; |
std::string body; |
||||||
headers response_headers; |
headers response_headers; |
||||||
code status_code; |
code status_code; |
||||||
}; |
}; |
||||||
}; |
}; |
||||||
|
|
||||||
#endif // _TOREST_HTTP_HPP_
|
#endif |
||||||
|
|||||||
@ -1,70 +1,75 @@ |
|||||||
|
#ifndef _TR_SESSION_HPP_ |
||||||
|
#define _TR_SESSION_HPP_ |
||||||
|
|
||||||
#include <http.hpp> |
#include <http.hpp> |
||||||
#include <util.hpp> |
#include <util.hpp> |
||||||
|
|
||||||
namespace tr{ |
namespace tr { |
||||||
namespace session { |
namespace session { |
||||||
template<class torrent_session, class request, class response> |
template <class torrent_session, class request, class response> |
||||||
void get(torrent_session&session,response resp,request req){ |
void get(torrent_session &session, response resp, request req) { |
||||||
auto http_response = http::response(); |
auto http_response = http::response(); |
||||||
if (session.is_valid()) { |
if (session.is_valid()) { |
||||||
const auto settings = session.get_settings(); |
const auto settings = session.get_settings(); |
||||||
http_response.set_body({ |
http_response.set_body({ |
||||||
{ "paused", session.is_paused() }, |
{"paused", session.is_paused()}, |
||||||
{ "port", session.listen_port() }, |
{"port", session.listen_port()}, |
||||||
{ "dht_enabled", session.is_dht_running() }, |
{"dht_enabled", session.is_dht_running()}, |
||||||
{ "down_limit", settings.get_int(settings.download_rate_limit) }, |
{"down_limit", settings.get_int(settings.download_rate_limit)}, |
||||||
{ "up_limit", settings.get_int(settings.upload_rate_limit) }, |
{"up_limit", settings.get_int(settings.upload_rate_limit)}, |
||||||
{ "torrents", session.get_torrents().size() }, //TODO Optimize
|
{"torrents", session.get_torrents().size()}, //TODO Optimize
|
||||||
// { "default_download_dir", default_download_dir.filename().string() },
|
// { "default_download_dir", default_download_dir.filename().string() },
|
||||||
// { "root_dir", root_dir.string() },
|
// { "root_dir", root_dir.string() },
|
||||||
}); |
}); |
||||||
} else { |
} else { |
||||||
auto response_code = http::code(http::service_unavailable); |
auto response_code = http::code(http::service_unavailable); |
||||||
http_response.set_body({{"code", response_code.first}, { "status", response_code.second}}); |
http_response.set_body({{"code", response_code.first}, {"status", response_code.second}}); |
||||||
http_response.set_status(response_code.first); |
http_response.set_status(response_code.first); |
||||||
} |
} |
||||||
*resp << http_response; |
*resp << http_response; |
||||||
} |
} |
||||||
template<class torrent_session, class request, class response> |
template <class torrent_session, class request, class response> |
||||||
void patch(torrent_session&session,response resp,request req){ |
void patch(torrent_session &session, response resp, request req) { |
||||||
auto http_response = http::response(); |
auto http_response = http::response(); |
||||||
if (!session.is_valid()) { |
if (!session.is_valid()) { |
||||||
auto response_code = http::code(http::service_unavailable); |
auto response_code = http::code(http::service_unavailable); |
||||||
http_response.set_body({{"code",response_code.first},{"status", response_code.second}}); |
http_response.set_body({{"code", response_code.first}, {"status", response_code.second}}); |
||||||
http_response.set_status(response_code.first); |
http_response.set_status(response_code.first); |
||||||
} else { |
} else { |
||||||
const auto json=util::json::parse(req->content); |
const auto json = util::json::parse(req->content); |
||||||
if(json.is_object() && !json.is_null()) { |
if (json.is_object() && !json.is_null()) { |
||||||
if(util::json::get("paused",json,session.is_paused())) |
if (util::json::get("paused", json, session.is_paused())) |
||||||
session.pause(); |
session.pause(); |
||||||
else |
else |
||||||
session.resume(); |
session.resume(); |
||||||
auto settings = session.get_settings(); |
auto settings = session.get_settings(); |
||||||
const auto new_listen_port=util::json::get("port",json,session.listen_port()); |
const auto new_listen_port = util::json::get("port", json, session.listen_port()); |
||||||
if(new_listen_port!=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));
|
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()); |
const auto new_dht_enabled = util::json::get("dht_enabled", json, session.is_dht_running()); |
||||||
if(new_dht_enabled!=session.is_dht_running()) |
if (new_dht_enabled != session.is_dht_running()) |
||||||
settings.set_bool(settings.enable_dht,new_dht_enabled); |
settings.set_bool(settings.enable_dht, new_dht_enabled); |
||||||
auto old_limit=settings.get_int(settings.download_rate_limit); |
auto old_limit = settings.get_int(settings.download_rate_limit); |
||||||
auto new_limit=util::json::get("down_limit",json,old_limit); |
auto new_limit = util::json::get("down_limit", json, old_limit); |
||||||
if(new_limit!=old_limit) |
if (new_limit != old_limit) |
||||||
settings.set_int(settings.download_rate_limit,new_limit); |
settings.set_int(settings.download_rate_limit, new_limit); |
||||||
old_limit=settings.get_int(settings.upload_rate_limit); |
old_limit = settings.get_int(settings.upload_rate_limit); |
||||||
new_limit=util::json::get("up_limit",json,old_limit); |
new_limit = util::json::get("up_limit", json, old_limit); |
||||||
if(new_limit!=old_limit) |
if (new_limit != old_limit) |
||||||
settings.set_int(settings.upload_rate_limit,new_limit); |
settings.set_int(settings.upload_rate_limit, new_limit); |
||||||
session.apply_settings(settings); |
session.apply_settings(settings); |
||||||
auto response_code = http::code(http::accepted); |
auto response_code = http::code(http::accepted); |
||||||
http_response.set_body({{"code",response_code.first},{"status", response_code.second}}); |
http_response.set_body({{"code", response_code.first}, {"status", response_code.second}}); |
||||||
http_response.set_status(response_code.first); |
http_response.set_status(response_code.first); |
||||||
} else { |
} else { |
||||||
auto response_code = http::code(http::bad_request); |
auto response_code = http::code(http::bad_request); |
||||||
http_response.set_body({{"code",response_code.first},{"status", response_code.second}}); |
http_response.set_body({{"code", response_code.first}, {"status", response_code.second}}); |
||||||
http_response.set_status(response_code.first); |
http_response.set_status(response_code.first); |
||||||
} |
} |
||||||
} |
} |
||||||
*resp << http_response; |
*resp << http_response; |
||||||
} |
|
||||||
} |
|
||||||
} |
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
||||||
@ -1,84 +1,82 @@ |
|||||||
#ifndef _BT_HELPERS_HPP_ |
#ifndef _TR_UTIL_HPP_ |
||||||
#define _BT_HELPERS_HPP_ |
#define _TR_UTIL_HPP_ |
||||||
|
|
||||||
#include <unordered_map> |
|
||||||
#include <boost/regex.hpp> |
#include <boost/regex.hpp> |
||||||
#include <json.hpp> |
#include <json.hpp> |
||||||
|
#include <unordered_map> |
||||||
|
|
||||||
namespace util { |
namespace util { |
||||||
class uri { |
class uri { |
||||||
public: |
public: |
||||||
auto static parse(const std::string &request_path){ |
auto static parse(const std::string &request_path) { |
||||||
std::unordered_map<std::string,std::string> options; |
std::unordered_map<std::string, std::string> options; |
||||||
std::string option, value; |
std::string option, value; |
||||||
bool collect=false; |
bool collect = false; |
||||||
for(auto &c:request_path){ |
for (auto &c : request_path) { |
||||||
switch(c){ |
switch (c) { |
||||||
case '&': |
case '&': |
||||||
options.insert(std::make_pair(option,value)); |
options.insert(std::make_pair(option, value)); |
||||||
option=""; |
option = ""; |
||||||
collect=true; |
collect = true; |
||||||
break; |
break; |
||||||
case '?': |
case '?': |
||||||
collect=true; |
collect = true; |
||||||
break; |
break; |
||||||
case '=': |
case '=': |
||||||
collect=false; |
collect = false; |
||||||
value=""; |
value = ""; |
||||||
break; |
break; |
||||||
default: |
default: |
||||||
if(collect) |
if (collect) |
||||||
option+=c; |
option += c; |
||||||
else |
else |
||||||
value+=c; |
value += c; |
||||||
break; |
break; |
||||||
} |
} |
||||||
} |
} |
||||||
if(!option.empty() || !value.empty()) |
if (!option.empty() || !value.empty()) |
||||||
options.insert(std::make_pair(option,value)); |
options.insert(std::make_pair(option, value)); |
||||||
return options; |
return options; |
||||||
} |
} |
||||||
}; |
}; |
||||||
const boost::regex regex("@(https?|ftp)://(-\\.)?([^\\s/?\\.#-]+\\.?)+(/[^\\s]*)?$@", boost::regex_constants::perl | boost::regex_constants::icase); |
const boost::regex regex("@(https?|ftp)://(-\\.)?([^\\s/?\\.#-]+\\.?)+(/[^\\s]*)?$@", boost::regex_constants::perl | boost::regex_constants::icase); |
||||||
|
|
||||||
class json { |
class json { |
||||||
public: |
public: |
||||||
/*! @brief Wrapper for nlohmann::json::parse. If the parse fails, result.is_null() will be */ |
/*! @brief Wrapper for nlohmann::json::parse. If the parse fails, result.is_null() will be */ |
||||||
static nlohmann::json parse(std::istream &istream) { |
static nlohmann::json parse(std::istream &istream) { |
||||||
try { |
try { |
||||||
return nlohmann::json::parse(istream); |
return nlohmann::json::parse(istream); |
||||||
} catch(const std::invalid_argument &exp) { |
} 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 */ |
/*! @brief wrapper around nlohmann::json::parse, but object returns null on throw */ |
||||||
static nlohmann::json parse(const std::string &string){ |
static nlohmann::json parse(const std::string &string) { |
||||||
try { |
try { |
||||||
return nlohmann::json::parse(string); |
return nlohmann::json::parse(string); |
||||||
} catch(const std::invalid_argument &exp) { |
} catch (const std::invalid_argument &exp) { |
||||||
|
|
||||||
} |
} |
||||||
return nlohmann::json(nullptr); |
return nlohmann::json(nullptr); |
||||||
} |
} |
||||||
static bool has_property(const std::string &key, const nlohmann::json &object){ |
static bool has_property(const std::string &key, const nlohmann::json &object) { |
||||||
auto it=object.find(key); |
auto it = object.find(key); |
||||||
return it!=object.end(); |
return it != object.end(); |
||||||
} |
} |
||||||
template<class T> |
template <class T> |
||||||
static auto get(const std::string &key, const nlohmann::json &object, const T &default_value){ |
static auto get(const std::string &key, const nlohmann::json &object, const T &default_value) { |
||||||
if(has_property(key,object)){ |
if (has_property(key, object)) { |
||||||
T r; |
T r; |
||||||
try{ |
try { |
||||||
r=object[key]; //TODO fix use json parser where a non-throwable exist
|
r = object[key]; //TODO fix use json parser where a non-throwable exist
|
||||||
} catch(const std::exception&){ |
} catch (const std::exception &) { |
||||||
return default_value; |
return default_value; |
||||||
} |
} |
||||||
return r; |
return r; |
||||||
} |
} |
||||||
return default_value; |
return default_value; |
||||||
} |
} |
||||||
}; |
}; |
||||||
} |
} |
||||||
|
|
||||||
#endif // _BT_HELPERS_HPP_
|
#endif |
||||||
|
|||||||
Loading…
Reference in new issue