1
/* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
4
* Copyright (C) 2011 Stewart Smith
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27
#include <drizzled/module/module.h>
28
#include <drizzled/module/context.h>
29
#include <drizzled/plugin/plugin.h>
30
#include <drizzled/plugin.h>
31
#include <drizzled/plugin/daemon.h>
32
#include <drizzled/sys_var.h>
33
#include <drizzled/gettext.h>
34
#include <drizzled/error.h>
35
#include <drizzled/query_id.h>
36
#include <drizzled/session.h>
37
#include <drizzled/internal/my_sys.h>
38
#include <drizzled/internal/m_string.h>
41
#include <boost/program_options.hpp>
42
#include <drizzled/module/option_map.h>
43
#include <drizzled/constrained_value.h>
46
#include <drizzled/execute.h>
47
#include <drizzled/sql/result_set.h>
49
#include <drizzled/plugin/listen.h>
50
#include <drizzled/plugin/client.h>
51
#include <drizzled/catalog/local.h>
53
#include <drizzled/pthread_globals.h>
54
#include <boost/bind.hpp>
57
#include <drizzled/version.h>
58
#include <plugin/json_server/json/json.h>
60
namespace po= boost::program_options;
61
using namespace drizzled;
64
namespace drizzle_plugin
69
static port_constraint port;
71
static in_port_t getPort(void)
76
extern "C" void process_request(struct evhttp_request *req, void* );
77
extern "C" void process_root_request(struct evhttp_request *req, void* );
78
extern "C" void process_api01_version_req(struct evhttp_request *req, void* );
79
extern "C" void process_api01_sql_req(struct evhttp_request *req, void* );
81
extern "C" void process_request(struct evhttp_request *req, void* )
83
struct evbuffer *buf = evbuffer_new();
84
if (buf == NULL) return;
85
evbuffer_add_printf(buf, "Requested: %s\n", evhttp_request_uri(req));
86
evhttp_send_reply(req, HTTP_OK, "OK", buf);
89
extern "C" void process_root_request(struct evhttp_request *req, void* )
91
struct evbuffer *buf = evbuffer_new();
92
if (buf == NULL) return;
96
output.append("<html><head><title>JSON DATABASE interface demo</title></head>"
98
"<script lang=\"javascript\">"
99
"function to_table(obj) {"
100
" var str = '<table>';"
101
"for (var r=0; r< obj.length; r++) {"
103
" for (var c=0; c < obj[r].length; c++) {"
104
" str+= '<td>' + obj[r][c] + '</td>';"
111
"function run_query()\n"
113
"var url = document.getElementById(\"baseurl\").innerHTML;\n"
114
"var query= document.getElementById(\"query\").value;\n"
115
"var xmlHttp = new XMLHttpRequest();\n"
116
"xmlHttp.onreadystatechange = function () {\n"
117
"if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {\n"
118
"var info = eval ( \"(\" + xmlHttp.responseText + \")\" );\n"
119
"document.getElementById( \"resultset\").innerHTML= to_table(info.result_set);\n"
122
"xmlHttp.open(\"POST\", url + \"/0.1/sql\", true);"
123
"xmlHttp.send(query);"
126
"function update_version()\n"
127
"{drizzle_version(document.getElementById(\"baseurl\").innerHTML);}\n\n"
128
"function drizzle_version($url)"
130
"var xmlHttp = new XMLHttpRequest();\n"
131
"xmlHttp.onreadystatechange = function () {\n"
132
"if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {\n"
133
"var info = eval ( \"(\" + xmlHttp.responseText + \")\" );\n"
134
"document.getElementById( \"drizzleversion\").innerHTML= info.version;\n"
137
"xmlHttp.open(\"GET\", $url + \"/0.1/version\", true);"
138
"xmlHttp.send(null);"
141
"<p>Drizzle Server at: <a id=\"baseurl\">http://localhost:8765</a></p>"
142
"<p>Drizzle server version: <a id=\"drizzleversion\"></a></p>"
143
"<p><textarea rows=\"3\" cols=\"40\" id=\"query\">"
144
"SELECT * from DATA_DICTIONARY.GLOBAL_STATUS;"
146
"<button type=\"button\" onclick=\"run_query();\">Execute Query</button>"
147
"<div id=\"resultset\"/>"
148
"<script lang=\"javascript\">update_version(); run_query();</script>"
151
evbuffer_add(buf, output.c_str(), output.length());
152
evhttp_send_reply(req, HTTP_OK, "OK", buf);
155
extern "C" void process_api01_version_req(struct evhttp_request *req, void* )
157
struct evbuffer *buf = evbuffer_new();
158
if (buf == NULL) return;
161
root["version"]= ::drizzled::version();
163
Json::StyledWriter writer;
164
std::string output= writer.write(root);
166
evbuffer_add(buf, output.c_str(), output.length());
167
evhttp_send_reply(req, HTTP_OK, "OK", buf);
170
extern "C" void process_api01_sql_req(struct evhttp_request *req, void* )
172
struct evbuffer *buf = evbuffer_new();
173
if (buf == NULL) return;
179
l= evbuffer_remove(req->input_buffer, buffer, 1024);
180
input.append(buffer, l);
183
drizzled::Session::shared_ptr _session= drizzled::Session::make_shared(drizzled::plugin::Listen::getNullClient(),
184
drizzled::catalog::local());
185
drizzled::identifier::user::mptr user_id= identifier::User::make_shared();
186
user_id->setUser("");
187
_session->setUser(user_id);
188
_session->set_schema("test");
190
drizzled::Execute execute(*(_session.get()), true);
192
drizzled::sql::ResultSet result_set(1);
194
/* Execute wraps the SQL to run within a transaction */
195
execute.run(input, result_set);
196
drizzled::sql::Exception exception= result_set.getException();
198
drizzled::error_t err= exception.getErrorCode();
201
root["sqlstate"]= exception.getSQLState();
203
if ((err != drizzled::EE_OK) && (err != drizzled::ER_EMPTY_QUERY))
205
root["error_message"]= exception.getErrorMessage();
206
root["error_code"]= exception.getErrorCode();
209
while (result_set.next())
211
Json::Value json_row;
212
for (size_t x= 0; x < result_set.getMetaData().getColumnCount(); x++)
214
if (not result_set.isNull(x))
216
json_row[x]= result_set.getString(x);
219
root["result_set"].append(json_row);
222
root["query"]= input;
224
Json::StyledWriter writer;
225
std::string output= writer.write(root);
227
evbuffer_add(buf, output.c_str(), output.length());
228
evhttp_send_reply(req, HTTP_OK, "OK", buf);
231
static void shutdown_event(int fd, short, void *arg)
233
struct event_base *base= (struct event_base *)arg;
234
event_base_loopbreak(base);
239
static void run(struct event_base *base)
241
internal::my_thread_init();
243
event_base_dispatch(base);
247
class JsonServer : public drizzled::plugin::Daemon
250
JsonServer(const JsonServer &);
251
JsonServer& operator=(const JsonServer &);
253
drizzled::thread_ptr json_thread;
255
struct evhttp *httpd;
256
struct event_base *base;
258
struct event wakeup_event;
261
JsonServer(in_port_t port_arg) :
262
drizzled::plugin::Daemon("JSON Server"),
270
if (pipe(wakeup_fd) < 0)
277
if ((returned_flags= fcntl(wakeup_fd[0], F_GETFL, 0)) < 0)
279
sql_perror("fcntl:F_GETFL");
283
if (fcntl(wakeup_fd[0], F_SETFL, returned_flags | O_NONBLOCK) < 0)
286
sql_perror("F_SETFL");
290
if ((base= event_init()) == NULL)
292
sql_perror("event_init()");
296
if ((httpd= evhttp_new(base)) == NULL)
298
sql_perror("evhttp_new()");
303
if ((evhttp_bind_socket(httpd, "0.0.0.0", getPort())) == -1)
305
sql_perror("evhttp_bind_socket()");
309
evhttp_set_cb(httpd, "/", process_root_request, NULL);
310
evhttp_set_cb(httpd, "/0.1/version", process_api01_version_req, NULL);
311
evhttp_set_cb(httpd, "/0.1/sql", process_api01_sql_req, NULL);
312
evhttp_set_gencb(httpd, process_request, NULL);
314
event_set(&wakeup_event, wakeup_fd[0], EV_READ | EV_PERSIST, shutdown_event, base);
315
event_base_set(base, &wakeup_event);
316
if (event_add(&wakeup_event, NULL) < 0)
318
sql_perror("event_add");
322
json_thread.reset(new boost::thread((boost::bind(&run, base))));
332
// If we can't write(), we will just muddle our way through the shutdown
335
if ((write(wakeup_fd[1], &buffer, 1)) == 1)
339
event_base_free(base);
344
static int json_server_init(drizzled::module::Context &context)
346
context.registerVariable(new sys_var_constrained_value_readonly<in_port_t>("port", port));
349
context.add(server= new JsonServer(port));
351
if (server and not server->init())
356
return bool(server) ? 0 : 1;
359
static void init_options(drizzled::module::option_context &context)
362
po::value<port_constraint>(&port)->default_value(8086),
363
_("Port number to use for connection or 0 for default (port 8086) "));
366
} /* namespace json_server */
367
} /* namespace drizzle_plugin */
369
DRIZZLE_DECLARE_PLUGIN
375
"JSON HTTP interface",
377
drizzle_plugin::json_server::json_server_init, /* Plugin Init */
379
drizzle_plugin::json_server::init_options /* config options */
381
DRIZZLE_DECLARE_PLUGIN_END;