diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index b02dc6d0ba..f76ec125cc 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -363,9 +363,9 @@ static int http_handler(void *cls, struct MHD_Connection *connection, const char #define ADMIN_SQLITE_TABLE_MYSQL_COLLATIONS "CREATE TABLE mysql_collations (Id INTEGER NOT NULL PRIMARY KEY , Collation VARCHAR NOT NULL , Charset VARCHAR NOT NULL , `Default` VARCHAR NOT NULL)" -#define ADMIN_SQLITE_TABLE_RESTAPI_ROUTES "CREATE TABLE restapi_routes (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT , active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1 , interval_ms INTEGER CHECK (interval_ms>-100 AND interval_ms<=100000000) NOT NULL , method VARCHAR NOT NULL CHECK (UPPER(method) IN ('GET','POST')), uri VARCHAR NOT NULL, script VARCHAR NOT NULL, comment VARCHAR NO NULL DEFAULT '')" +#define ADMIN_SQLITE_TABLE_RESTAPI_ROUTES "CREATE TABLE restapi_routes (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT , active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1 , interval_ms INTEGER CHECK (interval_ms>=100 AND interval_ms<=100000000) NOT NULL , method VARCHAR NOT NULL CHECK (UPPER(method) IN ('GET','POST')) , uri VARCHAR NOT NULL , script VARCHAR NOT NULL , comment VARCHAR NOT NULL DEFAULT '')" -#define ADMIN_SQLITE_TABLE_RUNTIME_RESTAPI_ROUTES "CREATE TABLE runtime_restapi_routes (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT , active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1 , interval_ms INTEGER CHECK (interval_ms>-100 AND interval_ms<=100000000) NOT NULL , method VARCHAR NOT NULL CHECK (UPPER(method) IN ('GET','POST')) , uri VARCHAR NOT NULL, script VARCHAR NOT NULL, comment VARCHAR NO NULL DEFAULT '')" +#define ADMIN_SQLITE_TABLE_RUNTIME_RESTAPI_ROUTES "CREATE TABLE runtime_restapi_routes (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT , active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1 , interval_ms INTEGER CHECK (interval_ms>=100 AND interval_ms<=100000000) NOT NULL , method VARCHAR NOT NULL CHECK (UPPER(method) IN ('GET','POST')) , uri VARCHAR NOT NULL , script VARCHAR NOT NULL , comment VARCHAR NOT NULL DEFAULT '')" #define ADMIN_SQLITE_TABLE_SCHEDULER "CREATE TABLE scheduler (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1 , interval_ms INTEGER CHECK (interval_ms>=100 AND interval_ms<=100000000) NOT NULL , filename VARCHAR NOT NULL , arg1 VARCHAR , arg2 VARCHAR , arg3 VARCHAR , arg4 VARCHAR , arg5 VARCHAR , comment VARCHAR NOT NULL DEFAULT '')" diff --git a/lib/ProxySQL_RESTAPI_Server.cpp b/lib/ProxySQL_RESTAPI_Server.cpp index c4f63a72e5..c61bf6bb17 100644 --- a/lib/ProxySQL_RESTAPI_Server.cpp +++ b/lib/ProxySQL_RESTAPI_Server.cpp @@ -20,15 +20,17 @@ extern ProxySQL_Admin *GloAdmin; class sync_resource : public http_resource { private: + void add_headers(std::shared_ptr &response) { + response->with_header("Content-Type", "application/json"); + response->with_header("Access-Control-Allow-Origin", "*"); + } + 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 method='" << req.get_method() << "' and active=1"; - bool rc=GloAdmin->admindb->execute_statement(ss.str().c_str(), &error, &cols, &affected_rows, &resultset); - if (!rc) { + ss << "SELECT * FROM runtime_restapi_routes WHERE uri='" << req.get_path_piece(1) << "' and method='" << req.get_method() << "' and active=1"; + std::unique_ptr resultset = std::unique_ptr(GloAdmin->admindb->execute_statement(ss.str().c_str(), &error)); + if (!resultset) { proxy_error("Cannot query script for given method [%s] and uri [%s]\n", req.get_method().c_str(), req.get_path_piece(1).c_str()); std::stringstream ss; if (error) { @@ -39,17 +41,20 @@ class sync_resource : public http_resource { ss << "{\"error\":\"The script for method [" << req.get_method() << "] and 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())); + auto response = std::shared_ptr(new string_response(ss.str())); + add_headers(response); + return response; } if (resultset && resultset->rows_count != 1) { std::stringstream ss; ss << "{\"error\":\"The script for method [" << req.get_method() << "] and route [" << req.get_path() << "] was not found. Rows count returned [" << resultset->rows_count << "]\" }"; proxy_error("Script for method [%s] and route [%s] was not found\n", req.get_method().c_str(), req.get_path().c_str()); - return std::shared_ptr(new string_response(ss.str())); + auto response = std::shared_ptr(new string_response(ss.str())); + add_headers(response); + return response; } script = resultset->rows[0]->fields[5]; interval_ms = atoi(resultset->rows[0]->fields[2]); - if (resultset) {delete resultset; resultset=NULL;} return std::shared_ptr(nullptr); } @@ -59,7 +64,10 @@ class sync_resource : public http_resource { params = _params; if (params.empty()) { proxy_error("Empty parameters\n"); - return std::shared_ptr(new string_response("{\"error\":\"Empty parameters\"}")); + + auto response = std::shared_ptr(new string_response("{\"error\":\"Empty parameters\"}")); + add_headers(response); + return response; } try { @@ -69,25 +77,36 @@ class sync_resource : public http_resource { 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())); + + auto response = std::shared_ptr(new string_response(ss.str())); + add_headers(response); + return response; } std::string script; int interval_ms; auto result=find_script(req, script, interval_ms); + + // result == nullpts means that script was found and we can execute it. continue. if (nullptr!=result) return result; int pipefd[2]; - if (pipe(pipefd) == -1) { + if (pipe(pipefd) == -1) { proxy_error("Cannot create pipe\n"); - return std::shared_ptr(new string_response("{\"error\":\"Cannot create pipe.\"}")); - } + + auto response = std::shared_ptr(new string_response("{\"error\":\"Cannot create pipe.\"}")); + add_headers(response); + return response; + } pid_t pid; if ((pid=fork()) == -1) { proxy_error("Cannot fork\n"); - return std::shared_ptr(new string_response("{\"error\":\"Cannot fork.\"}")); + + auto response = std::shared_ptr(new string_response("{\"error\":\"Cannot fork.\"}")); + add_headers(response); + return response; } char buf[65536] = {0}; @@ -103,7 +122,10 @@ class sync_resource : public http_resource { std::stringstream ss; 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())); + + auto response = std::shared_ptr(new string_response(ss.str())); + add_headers(response); + return response; } exit(EXIT_SUCCESS); } @@ -124,19 +146,28 @@ class sync_resource : public http_resource { 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())); + + auto response = std::shared_ptr(new string_response(ss.str())); + add_headers(response); + return response; } else if (rv == 0) { proxy_error("Timeout reading script output %s\n", script.c_str()); std::stringstream ss; ss << "{\"error\":\"Timeout reading script output. Script file: " << script << "\"}"; - return std::shared_ptr(new string_response(ss.str())); + + auto response = std::shared_ptr(new string_response(ss.str())); + add_headers(response); + return response; } 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.\"}")); + + auto response = std::shared_ptr(new string_response("{\"error\":\"Error reading pipe.\"}")); + add_headers(response); + return response; } // validate json correctness @@ -147,7 +178,10 @@ class sync_resource : public http_resource { 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())); + + auto response = std::shared_ptr(new string_response(ss.str())); + add_headers(response); + return response; } } close(pipefd[0]); @@ -155,7 +189,9 @@ class sync_resource : public http_resource { int status; waitpid(pid, &status, 0); } - return std::shared_ptr(new string_response(buf)); + auto response = std::shared_ptr(new string_response(buf)); + add_headers(response); + return response; } public: @@ -163,7 +199,11 @@ class sync_resource : public http_resource { 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())); + + auto response = std::shared_ptr(new string_response(ss.str().c_str())); + response->with_header("Content-Type", "application/json"); + response->with_header("Access-Control-Allow-Origin", "*"); + return response; } const std::shared_ptr render_GET(const http_request& req) { diff --git a/lib/ProxySQL_Restapi.cpp b/lib/ProxySQL_Restapi.cpp index ae536f2f91..2c851c377d 100644 --- a/lib/ProxySQL_Restapi.cpp +++ b/lib/ProxySQL_Restapi.cpp @@ -99,6 +99,7 @@ void ProxySQL_Restapi::load_restapi_to_runtime() { proxy_error("Error on %s : %s\n", query, error); } else { update_restapi_table(resultset.get()); + save_restapi_runtime_to_database(true); } }