diff --git a/src/libs/webview/rest_api.cpp b/src/libs/webview/rest_api.cpp new file mode 100644 index 0000000000..61f513fcb6 --- /dev/null +++ b/src/libs/webview/rest_api.cpp @@ -0,0 +1,122 @@ + +/*************************************************************************** + * rest_api.cpp - Webview REST API + * + * Created: Fri Mar 16 17:39:57 2018 + * Copyright 2006-2018 Tim Niemueller [www.niemueller.de] + ****************************************************************************/ + +/* 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 2 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 Library General Public License for more details. + * + * Read the full text in the LICENSE.GPL file in the doc directory. + */ + +#include "rest_api.h" + +#include + +namespace fawkes { +#if 0 /* just to make Emacs auto-indent happy */ +} +#endif + +std::pair> +WebviewRestApi::gen_regex(const std::string & path) +{ + if (path[0] != '/') { + throw Exception("Path '%s' must start with /", path.c_str()); + } + + std::regex to_re("\\{([^+\\}]+?)\\+?\\}"); + std::string m_path = path; + std::string re_url; + std::smatch match; + std::vector match_indexes; + while (regex_search(m_path, match, to_re)) { + std::string full_match = match[0]; + re_url += match.prefix(); + if (full_match[full_match.length()-2] == '+') { + re_url += "(.+?)"; + } else { + re_url += "([^/]+?)"; + } + match_indexes.push_back(match[1]); + m_path = match.suffix(); + } + re_url += m_path; + + return std::make_pair(std::regex(re_url), match_indexes); +} + +/** Check if an actual path matches an API path pattern. + * @param url requested + * @param api_path configured API path to check + * @param params object to set argument mappings + * @return true if the path cold be matched, false otherwise. + */ +bool +WebviewRestApi::path_match(const std::string & url, const path_regex &path_re, + WebviewRestParams& params) +{ + std::map path_args; + std::smatch matches; + if (std::regex_match(url, matches, path_re.first)) { + if (matches.size() != path_re.second.size() + 1) { + return false; + } + for (size_t i = 0; i < path_re.second.size(); ++i) { + path_args[path_re.second[i]] = matches[i+1].str(); + } + params.set_path_args(std::move(path_args)); + return true; + } else { + return false; + } +} + +/** Process REST API request. + * @param request incoming request + * @param rest_url the URL stripped of the base URL prefix + * @return reply + */ +WebReply * +WebviewRestApi::process_request(const WebRequest *request, const std::string & rest_url) +{ + WebviewRestParams params; + auto ri = std::find_if(routes_.begin(), routes_.end(), + [this, rest_url, ¶ms, request](auto r) { + if (std::get<0>(r) != request->method()) { + return false; + } + if (this->path_match(rest_url, std::get<2>(r), params)) { + return true; + } else { + return false; + } + }); + if (ri == routes_.end()) { + return new StaticWebReply(WebReply::HTTP_INTERNAL_SERVER_ERROR, "API " + rest_url + " unknown"); + } + params.set_query_args(request->get_values()); + std::unique_ptr reply = std::get<3>(*ri)(request->body(), params); + return reply.release(); +} + +/** Enable or disable pretty JSON printing globally. + * @param pretty true to enable + */ +void +WebviewRestApi::set_pretty_json(bool pretty) +{ + pretty_json_ = pretty; +} + +} // end of namespace fawkes diff --git a/src/libs/webview/rest_api.h b/src/libs/webview/rest_api.h index 60f40bce9d..de8b05423b 100644 --- a/src/libs/webview/rest_api.h +++ b/src/libs/webview/rest_api.h @@ -1,10 +1,9 @@ /*************************************************************************** - * reply.h - Web request reply + * rest_api.h - Webview REST API * * Created: Fri Mar 16 17:39:57 2018 * Copyright 2006-2018 Tim Niemueller [www.niemueller.de] - * ****************************************************************************/ /* This program is free software; you can redistribute it and/or modify @@ -23,7 +22,6 @@ #ifndef __LIBS_WEBVIEW_REST_API_H_ #define __LIBS_WEBVIEW_REST_API_H_ - #include #include @@ -31,13 +29,13 @@ #include #include -#include #include #include #include #include #include #include +#include namespace fawkes { #if 0 /* just to make Emacs auto-indent happy */ @@ -188,8 +186,8 @@ class WebviewRestApi * The API's name will be part of the URL, e.g., '/api/[COMPONENT-NAME]/...'. * @param logger logger for informative output */ - WebviewRestApi(const std::string &name, fawkes::Logger *logger) - : name_(name), logger_(logger), pretty_json_(false) {} + WebviewRestApi(const std::string &name, fawkes::Logger *logger) + : name_(name), logger_(logger), pretty_json_(false) {} /** Get name of component. * @return name of component. @@ -207,7 +205,7 @@ class WebviewRestApi */ void add_handler(WebRequest::Method method, std::string path, handler_func handler) { - routes_.push_back(std::make_tuple(method, path, handler)); + routes_.push_back(std::make_tuple(method, path, gen_regex(path), handler)); } /** Add handler function. @@ -220,7 +218,7 @@ class WebviewRestApi add_handler(WebRequest::Method method, std::string path, std::function handler) { - routes_.push_back(std::make_tuple(method, path, + routes_.push_back(std::make_tuple(method, path, gen_regex(path), [this, handler](const std::string &body, WebviewRestParams& m) -> std::unique_ptr { @@ -256,7 +254,7 @@ class WebviewRestApi void add_handler(WebRequest::Method method, std::string path, std::function handler) { - routes_.push_back(std::make_tuple(method, path, + routes_.push_back(std::make_tuple(method, path, gen_regex(path), [this, handler](const std::string &body, WebviewRestParams& m) -> std::unique_ptr { @@ -285,7 +283,7 @@ class WebviewRestApi void add_handler(WebRequest::Method method, std::string path, std::function handler) { - routes_.push_back(std::make_tuple(method, path, + routes_.push_back(std::make_tuple(method, path, gen_regex(path), [this, handler](const std::string &body, WebviewRestParams& m) -> std::unique_ptr { @@ -310,72 +308,24 @@ class WebviewRestApi })); } - /** Check if an actual path matches an API path pattern. - * @param url requested - * @param api_path configured API path to check - * @param params object to set argument mappings - * @return true if the path cold be matched, false otherwise. - */ - bool - path_match(const std::string & url, const std::string & api_path, WebviewRestParams& params) - { - std::map m; - std::vector url_s = str_split(url, '/'); - std::vector path_s = str_split(api_path, '/'); - if (url_s.size() != path_s.size()) return false; - for (size_t i = 0; i < url_s.size(); ++i) { - const std::string &p = path_s[i]; - const std::string &u = url_s[i]; - if (p.front() == '{' && p.back() == '}') { - m[p.substr(1, p.length() - 2)] = u; - } else if (p != u) { - return false; - } - } - params.set_path_args(std::move(m)); - return true; - } - - /** Process REST API request. - * @param request incoming request - * @param rest_url the URL stripped of the base URL prefix - * @return reply - */ WebReply * - process_request(const WebRequest *request, const std::string & rest_url) - { - WebviewRestParams params; - auto ri = std::find_if(routes_.begin(), routes_.end(), - [this, rest_url, ¶ms, request](auto r) { - if (std::get<0>(r) != request->method()) { - return false; - } - if (this->path_match(rest_url, std::get<1>(r), params)) { - return true; - } else { - return false; - } - }); - if (ri == routes_.end()) { - return new StaticWebReply(WebReply::HTTP_INTERNAL_SERVER_ERROR, "API " + rest_url + " unknown"); - } - params.set_query_args(request->get_values()); - std::unique_ptr reply = std::get<2>(*ri)(request->body(), params); - return reply.release(); - } + process_request(const WebRequest *request, const std::string & rest_url); - /** Enable or disable pretty JSON printing globally. - * @param pretty true to enable - */ - void set_pretty_json(bool pretty) - { - pretty_json_ = pretty; - } + void set_pretty_json(bool pretty); + + private: + typedef std::pair> path_regex; + + std::pair> + gen_regex(const std::string & path); + + bool path_match(const std::string & url, const path_regex &path_re, + WebviewRestParams& params); private: std::string name_; fawkes::Logger *logger_; - std::vector> routes_; + std::vector> routes_; bool pretty_json_; };