diff --git a/include/ProxySQL_RESTAPI_Server.hpp b/include/ProxySQL_RESTAPI_Server.hpp index 8ce7aedd6..fc0847e89 100644 --- a/include/ProxySQL_RESTAPI_Server.hpp +++ b/include/ProxySQL_RESTAPI_Server.hpp @@ -8,11 +8,10 @@ class ProxySQL_RESTAPI_Server { private: - //httpserver::webserver *ws; - httpserver::webserver * ws; + std::unique_ptr ws; int port; pthread_t thread_id; - httpserver::http_resource *hr; + std::unique_ptr endpoint; public: ProxySQL_RESTAPI_Server(int p); ~ProxySQL_RESTAPI_Server(); diff --git a/lib/ProxySQL_RESTAPI_Server.cpp b/lib/ProxySQL_RESTAPI_Server.cpp index 2fdd6b399..24136dfb6 100644 --- a/lib/ProxySQL_RESTAPI_Server.cpp +++ b/lib/ProxySQL_RESTAPI_Server.cpp @@ -18,91 +18,177 @@ using namespace httpserver; extern ProxySQL_Admin *GloAdmin; -class hello_world_resource : public http_resource { -public: - const std::shared_ptr render_GET(const http_request& req) { - return std::shared_ptr(new string_response("GET: Hello, World!\n")); - } - - const std::shared_ptr render_POST(const http_request& req) { - //TODO : validate json correctness in the req - - int pipefd[2]; - if (pipe(pipefd) == -1) { - return std::shared_ptr(new string_response("{\"error\":\"Cannot create pipe.\"}")); - } +class sync_resource : public http_resource { +private: + const std::shared_ptr find_script(const http_request& req, std::string& script, int &interval_ms) { + SQLite3_result *resultset=NULL; + int affected_rows; + int cols; + char *error=NULL; + std::stringstream ss; + ss << "SELECT * FROM restapi_routes WHERE uri='" << req.get_path_piece(1) << "' and active=1"; + bool rc=GloAdmin->admindb->execute_statement(ss.str().c_str(), &error, &cols, &affected_rows, &resultset); + if (!rc) { + proxy_error("Cannot query script for given path [%s]\n", req.get_path_piece(1)); + std::stringstream ss; + if (error) { + ss << "{\"error\":\"The script for route [" << req.get_path() << "] was not found. Error: " << error << "Error: \"}"; + proxy_error("Path %s, error %s\n", req.get_path().c_str(), error); + } + else { + ss << "{\"error\":\"The script for route [" << req.get_path() << "] was not found.\"}"; + proxy_error("Path %s\n", req.get_path().c_str()); + } + return std::shared_ptr(new string_response(ss.str())); + } + if (resultset && resultset->rows_count != 1) { + std::stringstream ss; + ss << "{\"error\":\"The script for route [" << req.get_path() << "] was not found. count = " << resultset->rows_count << "\" }"; + proxy_error("Script for route %s was not found\n", req.get_path().c_str()); + return std::shared_ptr(new string_response(ss.str())); + } + script = resultset->rows[0]->fields[4]; + interval_ms = atoi(resultset->rows[0]->fields[2]); + if (resultset) {delete resultset; resultset=NULL;} + return std::shared_ptr(nullptr); + } - pid_t pid; - if ((pid=fork()) == -1) { - return std::shared_ptr(new string_response("{\"error\":\"Cannot fork.\"}")); + const std::shared_ptr process_request(const http_request& req, const std::string& _params) { + std::string params = req.get_content(); + if (params.empty()) + params = _params; + if (params.empty()) { + proxy_error("Empty parameters\n"); + return std::shared_ptr(new string_response("{\"error\":\"Empty parameters\"}")); } - // validate json correctness try { - nlohmann::json valid=nlohmann::json::parse(req.get_content()); + nlohmann::json valid=nlohmann::json::parse(params); } catch(nlohmann::json::exception& e) { std::stringstream ss; ss << "{\"type\":\"in\", \"error\":\"" << e.what() << "\"}"; + proxy_error("Error parsing input json parameters. %s\n", ss.str().c_str()); return std::shared_ptr(new string_response(ss.str())); } + std::string script; + int interval_ms; + auto result=find_script(req, script, interval_ms); + if (nullptr!=result) + return result; + + int pipefd[2]; + if (pipe(pipefd) == -1) { + proxy_error("Cannot create pipe\n"); + return std::shared_ptr(new string_response("{\"error\":\"Cannot create pipe.\"}")); + } + + pid_t pid; + if ((pid=fork()) == -1) { + proxy_error("Cannot fork\n"); + return std::shared_ptr(new string_response("{\"error\":\"Cannot fork.\"}")); + } - char buf[1024] = {0}; + char buf[65536] = {0}; if (pid == 0) { dup2(pipefd[1], STDOUT_FILENO); close(pipefd[0]); close(pipefd[1]); - SQLite3_result *resultset=NULL; - int affected_rows; - int cols; - char *error=NULL; - std::stringstream ss; - ss << "SELECT * FROM restapi_routes WHERE uri='" << req.get_path_piece(1) << "'"; - bool rc=GloAdmin->admindb->execute_statement(ss.str().c_str(), &error, &cols, &affected_rows, &resultset); - if (!rc) { - proxy_error("Cannot query script for given path [%s]\n", req.get_path_piece(1)); - } - if (!resultset || resultset->rows_count == 0) { - std::stringstream ss; - ss << "{\"error\":\"The script for route [" << req.get_path() << "] was not found.\"}"; - return std::shared_ptr(new string_response(ss.str())); - } - std::string script = resultset->rows[0]->fields[4]; - char* const args[] = {const_cast("a"), const_cast(req.get_content().c_str()), NULL}; - if (execve(resultset->rows[0]->fields[4], args, NULL) == -1) { + char* const args[] = {const_cast(script.c_str()), const_cast(params.c_str()), NULL}; + if (execve(script.c_str(), args, NULL) == -1) { char path_buffer[PATH_MAX]; char* cwd = getcwd(path_buffer, sizeof(path_buffer)-1); std::stringstream ss; - ss << "{\"error\":\"Error calling execve().\", \"cwd\":\"" << cwd << "\", \"file\":\"" << resultset->rows[0]->fields[4] << "\"}"; + ss << "{\"error\":\"Error calling execve().\", \"cwd\":\"" << cwd << "\", \"file\":\"" << script << "\"}"; + proxy_error("%s\n", ss.str().c_str()); return std::shared_ptr(new string_response(ss.str())); } exit(EXIT_SUCCESS); } else { close(pipefd[1]); - int nbytes = read(pipefd[0], buf, sizeof(buf) - 1); - if (nbytes == -1) { - return std::shared_ptr(new string_response("{\"error\":\"Error reading pipe.\"}")); - } - // validate json correctness - try { - nlohmann::json j=nlohmann::json::parse(buf); + fd_set set; + + FD_ZERO(&set); + FD_SET(pipefd[0], &set); + + struct timeval timeout; + timeout.tv_sec=interval_ms/1000; + timeout.tv_usec=(interval_ms%1000)*1000; + + int rv = select(pipefd[0]+1,&set,NULL,NULL,&timeout); + if (rv == -1) { + proxy_error("Error calling select for path %s\n", req.get_path().c_str()); + std::stringstream ss; + ss << "{\"error\":\"Error calling select().\", \"path\":\"" << req.get_path() << "\"}"; + return std::shared_ptr(new string_response(ss.str())); } - catch(nlohmann::json::exception& e) { + else if (rv == 0) { + proxy_error("Timeout reading script output %s\n", script.c_str()); std::stringstream ss; - ss << "{\"type\":\"out\", \"error\":\"" << e.what() << "\"}"; - proxy_error("Error parsing script output. %s\n", buf); + ss << "{\"error\":\"Timeout reading script output. Script file: " << script << "\"}"; return std::shared_ptr(new string_response(ss.str())); } - + else { + int nbytes = read(pipefd[0], buf, sizeof(buf) - 1); + if (nbytes == -1) { + proxy_error("Error reading pipe\n"); + return std::shared_ptr(new string_response("{\"error\":\"Error reading pipe.\"}")); + } + + // validate json correctness + try { + nlohmann::json j=nlohmann::json::parse(buf); + } + catch(nlohmann::json::exception& e) { + std::stringstream ss; + ss << "{\"type\":\"out\", \"error\":\"" << e.what() << "\"}"; + proxy_error("Error parsing script output. %s\n", buf); + return std::shared_ptr(new string_response(ss.str())); + } + } close(pipefd[0]); - wait(NULL); + + int status; + waitpid(pid, &status, 0); } return std::shared_ptr(new string_response(buf)); } + +public: + const std::shared_ptr render(const http_request& req) { + proxy_info("Render generic request with method %s for uri %s\n", req.get_method().c_str(), req.get_path().c_str()); + std::stringstream ss; + ss << "{\"error\":\"HTTP method " << req.get_method().c_str() << " is not implemented\"}"; + return std::shared_ptr(new string_response(ss.str().c_str())); + } + + const std::shared_ptr render_GET(const http_request& req) { + auto args = req.get_args(); + + size_t last = 0; + std::stringstream params; + params << "{"; + for (auto arg : args) { + params << "\"" << arg.first << "\":\"" << arg.second << "\""; + if (last < args.size()-1) { + params << ","; + last++; + } + } + params << "}"; + + return process_request(req, params.str()); + } + + const std::shared_ptr render_POST(const http_request& req) { + std::string params=req.get_content(); + return process_request(req, params); + } + }; void * restapi_server_thread(void *arg) { @@ -112,25 +198,16 @@ void * restapi_server_thread(void *arg) { } ProxySQL_RESTAPI_Server::ProxySQL_RESTAPI_Server(int p) { + ws = std::unique_ptr(new webserver(create_webserver(p))); + auto sr = new sync_resource(); + + endpoint = std::unique_ptr(sr); - // for now, this is COMPLETELY DISABLED - // just adding a POC -// return; - - //ws = NULL; - port = p; - ws = new webserver(create_webserver(p)); - //hello_world_resource hwr; - hr = new hello_world_resource(); - - //ws->register_resource("/hello", &hwr); - ws->register_resource("/hello", hr, true); - if (pthread_create(&thread_id, NULL, restapi_server_thread, ws) !=0 ) { - perror("Thread creation"); - exit(EXIT_FAILURE); + ws->register_resource("/sync", endpoint.get(), true); + if (pthread_create(&thread_id, NULL, restapi_server_thread, ws.get()) !=0 ) { + perror("Thread creation"); + exit(EXIT_FAILURE); } - //webserver ws2 = create_webserver(8080); - //webserws = create_webserver(8080); } ProxySQL_RESTAPI_Server::~ProxySQL_RESTAPI_Server() { diff --git a/scripts/metrics.py b/scripts/metrics.py index 6ea4c421a..49fc50ce2 100755 --- a/scripts/metrics.py +++ b/scripts/metrics.py @@ -4,7 +4,6 @@ import sys import subprocess import json - if len(sys.argv) > 1: params=json.loads(sys.argv[1]) out=''