7#define GCDEBUG(...) Ase::debug ("gc", __VA_ARGS__)
8#define GCDEBUG_ENABLED() Ase::debug_key_enabled ("gc")
12static String subprotocol_authentication;
15jsonapi_set_subprotocol (
const String &subprotocol)
17 subprotocol_authentication = subprotocol;
21class JsonapiConnection;
24static JsonapiConnectionP current_message_conection;
27is_localhost (
const String &url,
int port)
29 return Re::search (
"^https?://(localhost|127\\.0\\.0\\.1)(:[0-9]+)?/", url, Re::I) >= 0;
35 log (
const String &message)
override
37 printerr (
"%s: %s\n", nickname(), message);
42 using namespace AnsiColors;
43 const auto C1 = color (BOLD), C0 = color (BOLD_OFF);
44 const Info info = get_info();
45 const String origin = info.header (
"Origin") +
"/";
46 const bool localhost_origin = is_localhost (origin, info.lport);
47 const bool subproto_ok = (info.subs.size() == 0 && subprotocol_authentication.
empty()) ||
48 (info.subs.size() == 1 && subprotocol_authentication == info.subs[0]);
49 if (localhost_origin && subproto_ok)
53 if (!localhost_origin) why =
"Bad Origin";
54 else if (!subproto_ok) why =
"Bad Subprotocol";
55 const String ua = info.header (
"User-Agent");
57 log (
string_format (
"%sREJECT:%s %s:%d/ (%s) - %s", C1, C0, info.remote, info.rport, why, ua));
63 using namespace AnsiColors;
64 const auto C1 = color (BOLD), C0 = color (BOLD_OFF);
65 const Info info = get_info();
66 const String ua = info.header (
"User-Agent");
68 log (
string_format (
"%sACCEPT:%s %s:%d/ - %s", C1, C0, info.remote, info.rport, ua));
73 using namespace AnsiColors;
74 const auto C1 = color (BOLD), C0 = color (BOLD_OFF);
77 trigger_destroy_hooks();
80 message (
const String &message)
override
92 if (main_loop->has_quit())
94 current_message_conection = data->conp;
95 data->reply = this->handle_jsonipc (data->message);
96 current_message_conection =
nullptr;
101 const uint64_t timeout_us = 50 * 1000;
103 const int ret = data->sem.wait_for (timeout_us);
106 if (data->reply.empty() && main_loop->has_quit())
107 data->reply =
"{id:0,error:{code:-32601,message:\"Method not found: endpoint shutting down\"}}\n";
108 }
while (data->reply.empty());
110 if (!data->reply.empty())
121 trigger_destroy_hooks();
126 const bool starting_gc = imap_.mark_unused();
127 GCDEBUG (
"%s: imap_=%d%s\n", __func__, imap_.size(), starting_gc ?
" (duplicate)" :
"");
133 const size_t preerved = imap_.purge_unused (ids);
134 GCDEBUG (
"%s: considered=%d retained=%d purged=%d active=%d\n", __func__,
135 ids.size(), preerved, ids.size() - preerved, imap_.size());
139 trigger_lookup (
const String &
id)
141 for (
auto it = triggers_.begin(); it != triggers_.end(); it++)
147 trigger_remove (
const String &
id)
149 trigger_lookup (
id).destroy();
152 trigger_create (
const String &
id)
154 using namespace Jsonipc;
158 const int logflags = logflags_;
160 auto trigger_remote = [selfw, id, logflags] (
ValueS &&args)
164 const String msg = jsonobject_to_string (
"method",
id ,
"params", args);
167 selfp->send_text (msg);
169 JsTrigger trigger = JsTrigger::create (
id, trigger_remote);
170 triggers_.push_back (trigger);
172 auto erase_trigger = [selfw, id, logflags] ()
176 if (selfp->is_open())
179 const String msg = jsonobject_to_string (
"method",
"Jsonapi/Trigger/killed",
"params", args);
182 selfp->send_text (msg);
184 Aux::erase_first (selfp->triggers_, [
id] (
auto &t) { return id == t.id(); });
186 trigger.ondestroy (erase_trigger);
189 trigger_destroy_hooks()
192 old.swap (triggers_);
193 for (
auto &trigger : old)
195 custom_data_destroy();
205#define ERROR500(WHAT) \
206 Jsonipc::bad_invocation (-32500, \
208 ASE_CPP_STRINGIFY (__LINE__) ": " \
209 "Internal Server Error: " \
211#define assert_500(c) (__builtin_expect (static_cast<bool> (c), 1) ? (void) 0 : throw ERROR500 (#c) )
216 using namespace Jsonipc;
219 dispatcher->add_method (
"Jsonapi/renew-gc",
222 assert_500 (current_message_conection);
223 if (cbi.n_args() > 0)
225 const auto ret = current_message_conection->renew_gc ();
226 cbi.set_result (to_json (ret, cbi.allocator()).Move());
228 dispatcher->add_method (
"Jsonapi/report-gc",
231 assert_500 (current_message_conection);
232 if (cbi.n_args() != 1)
234 const auto ids = from_json<std::vector<size_t>> (cbi.ntharg (0));
235 const auto ret = current_message_conection->report_gc (ids);
236 cbi.set_result (to_json (ret, cbi.allocator()).Move());
238 dispatcher->add_method (
"Jsonapi/initialize",
241 assert_500 (current_message_conection);
242 Server &server = ASE_SERVER;
244 cbi.set_result (to_json (serverp, cbi.allocator()).Move());
246 dispatcher->add_method (
"Jsonapi/Trigger/create",
249 assert_500 (current_message_conection);
250 const String triggerid = cbi.n_args() == 1 ? from_json<String> (cbi.ntharg (0)) :
"";
251 if (triggerid.compare (0, 17,
"Jsonapi/Trigger/_") != 0)
253 current_message_conection->trigger_create (triggerid);
255 dispatcher->add_method (
"Jsonapi/Trigger/remove",
258 assert_500 (current_message_conection);
259 const String triggerid = cbi.n_args() == 1 ? from_json<String> (cbi.ntharg (0)) :
"";
260 if (triggerid.compare (0, 17,
"Jsonapi/Trigger/_") != 0)
262 current_message_conection->trigger_remove (triggerid);
270JsonapiConnection::handle_jsonipc (
const std::string &message)
277 CoalesceNotifies coalesce_notifies;
278 reply = make_dispatcher()->dispatch_message (message);
282 const char *errorat =
strstr (reply.c_str(),
"\"error\":{");
283 if (errorat && errorat > reply.c_str() && (errorat[-1] ==
',' || errorat[-1] ==
'{'))
285 using namespace AnsiColors;
290 log (
string_format (
"← %s", reply.size() > 1024 ? reply.substr (0, 1020) +
"..." + reply.back() : reply));
305 Impl& operator= (
const Impl&) =
delete;
318 while (!destroyhooks.empty())
320 VoidFunc destroyhook = destroyhooks.back();
321 destroyhooks.pop_back();
328JsTrigger::ondestroy (
const VoidFunc &vf)
332 p_->destroyhooks.push_back (vf);
336JsTrigger::call (
ValueS &&args)
const
340 p_->func (std::move (args));
344JsTrigger::create (
const String &triggerid,
const JsTrigger::Impl::Func &f)
353JsTrigger::id ()
const
355 return p_ ? p_->id :
"";
365JsTrigger::operator
bool () const noexcept
367 return p_ && p_->func;
371ConvertJsTrigger::lookup (
const String &triggerid)
373 if (current_message_conection)
374 return current_message_conection->trigger_lookup (triggerid);
380jsonapi_connection_data ()
382 if (current_message_conection)
383 return current_message_conection.get();
388jsonapi_connection_sender ()
391 JsonapiConnectionW conw = current_message_conection;
392 return [conw] (
const String &blob) {
393 JsonapiConnectionP conp = conw.lock();
394 return conp ? conp->send_binary (blob) :
false;
414 for (
size_t i = 1; i <= 99; i++)
415 tmap[1000 - i] = string_format (
"%d", i);
417 for (
auto it = tmap.begin(), next = it; it != tmap.end() ? ++
next, 1 : 0; it =
next)
DataListContainer - typesafe storage and retrieval of arbitrary members.
Callback mechanism for Jsonapi/Jsonipc.
static ssize_t search(const String ®ex, const String &input, Flags=DEFAULT)
Find regex in input and return match position >= 0 or return < 0 otherwise.
bool send_text(const String &message)
Returns true if text message was sent.
Keep track of temporary instances during IpcDispatcher::dispatch_message().
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
#define TEST_INTEGRITY(FUNC)
Register func as an integrity test.
std::string color(Colors acolor, Colors c1, Colors c2, Colors c3, Colors c4, Colors c5, Colors c6)
Return ANSI code for the specified color if stdout & stderr should be colorized, see colorize_tty().
size_t erase_first(C &container, const std::function< bool(typename C::value_type const &value)> &pred)
Erase first element for which pred() is true in vector or list.
The Anklang C++ API namespace.
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
JobQueue main_jobs(call_main_loop)
Execute a job callback in the event loop.
std::string String
Convenience alias for std::string.
T shared_from_this(T... args)
Context for calling C++ functions from Json.
Jsonipc exception that is relayed to caller when thrown during invocations.
#define TASSERT(cond)
Unconditional test assertion, enters breakpoint if not fullfilled.