mirror of
https://github.com/gnosygnu/xowa.git
synced 2026-03-02 03:49:30 +00:00
Embeddable: Create core dbs in proper subdirectory
This commit is contained in:
@@ -13,3 +13,72 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*;
|
||||
import gplx.core.primitives.*; import gplx.core.js.*;
|
||||
import gplx.gfui.*; import gplx.gfui.kits.core.*; import gplx.gfui.controls.gxws.*;
|
||||
import gplx.xowa.apps.servers.tcp.*;
|
||||
import gplx.xowa.apps.servers.http.*; import gplx.xowa.guis.views.*;
|
||||
public class Gxw_html_server implements Gxw_html {
|
||||
private Xosrv_socket_wtr wtr; private Gfo_usr_dlg usr_dlg;
|
||||
private final Js_wtr js_wtr = new Js_wtr();
|
||||
public Gxw_html_server(Gfo_usr_dlg usr_dlg, Xosrv_socket_wtr wtr) {
|
||||
this.usr_dlg = usr_dlg; this.wtr = wtr;
|
||||
}
|
||||
public void Html_doc_html_load_by_mem(String html) {Exec_as_str("location.reload(true);");} // HACK: force reload of page
|
||||
public void Html_doc_html_load_by_url(Io_url path, String html) {Exec_as_str("location.reload(true);");} // HACK: force reload of page
|
||||
public byte Html_doc_html_load_tid() {return html_doc_html_load_tid;} private byte html_doc_html_load_tid;
|
||||
public void Html_doc_html_load_tid_(byte v) {html_doc_html_load_tid = v;}
|
||||
public void Html_dispose() {}
|
||||
public void Html_js_enabled_(boolean v) {}
|
||||
public String Html_js_eval_proc_as_str(String name, Object... args) {return Exec_as_str(js_wtr.Write_statement_return_func(name, args).To_str_and_clear());} // TODO_OLD: add other params
|
||||
public boolean Html_js_eval_proc_as_bool(String name, Object... args) {return Exec_as_bool(js_wtr.Write_statement_return_func(name, args).To_str_and_clear());}
|
||||
public String Html_js_eval_script(String script) {return Exec_as_str(script);}
|
||||
public Object Html_js_eval_script_as_obj(String script) {return Exec_as_str(script);}
|
||||
public void Html_js_cbks_add(String js_func_name, Gfo_invk invk) {}
|
||||
public String Html_js_send_json(String name, String data) {throw Err_.new_unimplemented();}
|
||||
public void Html_invk_src_(Gfo_evt_itm v) {}
|
||||
public GxwCore_base Core() {throw Err_.new_unimplemented();}
|
||||
public GxwCbkHost Host() {throw Err_.new_unimplemented();} public void Host_set(GxwCbkHost host) {throw Err_.new_unimplemented();}
|
||||
public Object UnderElem() {throw Err_.new_unimplemented();}
|
||||
public String TextVal() {throw Err_.new_unimplemented();} public void TextVal_set(String v) {throw Err_.new_unimplemented();}
|
||||
public void EnableDoubleBuffering() {throw Err_.new_unimplemented();}
|
||||
private boolean Exec_as_bool(String s) {
|
||||
Exec_as_str(s);
|
||||
return true; // NOTE: js is async, so immediate return value is not possible; return true for now;
|
||||
}
|
||||
private String Exec_as_str(String s) {
|
||||
if (wtr == null) return ""; // HACK: handles http_server
|
||||
s = "(function () {" + s + "})();"; // NOTE: dependent on firefox_addon which does 'var result = Function("with(arguments[0]){return "+cmd_text+"}")(session.window);'; DATE:2014-01-28
|
||||
gplx.core.threads.Thread_adp_.Sleep(50); // NOTE: need to sleep, else images won't actually show up on screen; PAGE:nethackwiki.com:Weapons; DATE:2015-08-23
|
||||
Xosrv_msg msg = Xosrv_msg.new_(Xosrv_cmd_types.Browser_exec, Bry_.Empty, Bry_.Empty, Bry_.Empty, Bry_.Empty, Bry_.new_u8(s));
|
||||
usr_dlg.Note_many("", "", "sending browser.js: msg=~{0}", s);
|
||||
wtr.Write(msg);
|
||||
return "";
|
||||
}
|
||||
public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
|
||||
if (ctx.Match(k, Invk_set)) {}
|
||||
else return Gfo_invk_.Rv_unhandled;
|
||||
return this;
|
||||
} private static final String Invk_set = "set";
|
||||
public static void Init_gui_for_server(Xoae_app app, Xosrv_socket_wtr wtr) {
|
||||
Mem_kit mem_kit = (Mem_kit)Gfui_kit_.Mem();
|
||||
mem_kit.New_html_impl_prototype_(new Gxw_html_server(app.Usr_dlg(), wtr)); // NOTE: set prototype before calling Kit_
|
||||
app.Gui_mgr().Kit_(mem_kit);
|
||||
}
|
||||
public static void Assert_tab(Xoae_app app, Xoae_page page) {
|
||||
Xog_win_itm browser_win = app.Gui_mgr().Browser_win();
|
||||
if (browser_win.Active_tab() == null) { // no active tab
|
||||
Xowe_wiki wiki = page.Wikie(); // take wiki from current page; NOTE: do not take from browser_win.Active_tab().Wiki(); DATE:2015-02-23
|
||||
browser_win.Tab_mgr().Tabs_new_init(wiki, page); // create at least one active tab; DATE:2014-07-30
|
||||
}
|
||||
}
|
||||
public static Xog_tab_itm Assert_tab2(Xoae_app app, Xowe_wiki wiki) {
|
||||
Xog_win_itm browser_win = app.Gui_mgr().Browser_win();
|
||||
Xog_tab_itm rv = browser_win.Active_tab();
|
||||
if (rv == null) { // no active tab
|
||||
Xoae_page page = Xoae_page.New(wiki, wiki.Ttl_parse(Bry_.new_a7("Empty_tab")));
|
||||
rv = browser_win.Tab_mgr().Tabs_new_init(wiki, page); // create at least one active tab; DATE:2014-07-30
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,22 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.http; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
class File_retrieve_mode {
|
||||
public static String Xto_str(byte v) {
|
||||
switch (v) {
|
||||
case Mode_skip: return "skip";
|
||||
case Mode_wait: return "wait";
|
||||
case Mode_async_server: return "async_server";
|
||||
default: throw Err_.new_unimplemented();
|
||||
}
|
||||
}
|
||||
public static byte Xto_byte(String s) {
|
||||
if (String_.Eq(s, "skip")) return Mode_skip;
|
||||
else if (String_.Eq(s, "wait")) return Mode_wait;
|
||||
else if (String_.Eq(s, "async_server")) return Mode_async_server;
|
||||
else throw Err_.new_unimplemented();
|
||||
}
|
||||
public static final byte Mode_skip = 1, Mode_wait = 2, Mode_async_server = 3;
|
||||
public static Keyval[] Options__list = Keyval_.Ary(Keyval_.new_("wait"), Keyval_.new_("skip"), Keyval_.new_("async_server", "async server"));
|
||||
}
|
||||
|
||||
@@ -13,3 +13,13 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.http; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import gplx.langs.jsons.*;
|
||||
public class Http_data__client {
|
||||
public Http_data__client(byte[] server_host, String ip_address_str) {
|
||||
this.server_host = server_host;
|
||||
this.ip_address = Bry_.new_a7(ip_address_str);
|
||||
}
|
||||
public byte[] Server_host() {return server_host;} private final byte[] server_host;
|
||||
public byte[] Ip_address() {return ip_address;} private final byte[] ip_address;
|
||||
}
|
||||
|
||||
@@ -13,3 +13,36 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.http; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import gplx.xowa.files.*;
|
||||
class Http_file_utl {
|
||||
public static byte[] To_mime_type_by_path_as_bry(byte[] path_bry) {
|
||||
int dot_pos = Bry_find_.Find_bwd(path_bry, Byte_ascii.Dot);
|
||||
return dot_pos == Bry_find_.Not_found ? Mime_octet_stream : To_mime_type_by_ext_as_bry(path_bry, dot_pos, path_bry.length);
|
||||
}
|
||||
public static byte[] To_mime_type_by_ext_as_bry(byte[] ext_bry, int bgn, int end) {
|
||||
Object o = mime_hash.Get_by_mid(ext_bry, bgn, end);
|
||||
return o == null ? Mime_octet_stream : (byte[])o;
|
||||
}
|
||||
private static final byte[]
|
||||
Mime_octet_stream = Xof_ext_.Mime_type__ary[Xof_ext_.Id_unknown]
|
||||
, Mime_html = Bry_.new_a7("text/html")
|
||||
, Mime_css = Bry_.new_a7("text/css")
|
||||
, Mime_js = Bry_.new_a7("application/javascript")
|
||||
;
|
||||
private static final Hash_adp_bry mime_hash = Mime_hash__new();
|
||||
private static Hash_adp_bry Mime_hash__new() {
|
||||
Hash_adp_bry rv = Hash_adp_bry.ci_a7();
|
||||
int len = Xof_ext_.Id__max;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
rv.Add_bry_obj
|
||||
( Bry_.Add(Byte_ascii.Dot, Xof_ext_.Bry__ary[i])
|
||||
, Xof_ext_.Mime_type__ary[i]);
|
||||
}
|
||||
rv.Add_str_obj(".htm" , Mime_html);
|
||||
rv.Add_str_obj(".html" , Mime_html);
|
||||
rv.Add_str_obj(".css" , Mime_css);
|
||||
rv.Add_str_obj(".js" , Mime_js);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,144 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
/*
|
||||
This file is part of XOWA: the XOWA Offline Wiki Application
|
||||
Copyright (C) 2013 matthiasjasny@gmail.com; gnosygnu@gmail.com
|
||||
|
||||
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
|
||||
or alternatively under the terms of the Apache License Version 2.0.
|
||||
|
||||
You may use XOWA according to either of these licenses as is most appropriate
|
||||
for your project on a case-by-case basis.
|
||||
|
||||
The terms of each license can be found in the following files:
|
||||
|
||||
GPLv3 License: LICENSE-GPLv3.txt
|
||||
Apache License: LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.http; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import gplx.core.threads.*; import gplx.core.net.*; import gplx.core.primitives.*; import gplx.core.envs.*;
|
||||
import gplx.langs.jsons.*; import gplx.langs.htmls.encoders.*;
|
||||
import gplx.xowa.wikis.pages.*;
|
||||
public class Http_server_mgr implements Gfo_invk {
|
||||
private final Object thread_lock = new Object();
|
||||
private final Gfo_usr_dlg usr_dlg;
|
||||
private Http_server_socket wkr;
|
||||
private byte retrieve_mode = File_retrieve_mode.Mode_wait;
|
||||
private boolean running, init_gui_needed = true;
|
||||
public Http_server_mgr(Xoae_app app) {
|
||||
this.app = app;
|
||||
this.usr_dlg = app.Usr_dlg();
|
||||
this.request_parser = new Http_request_parser(server_wtr, false);
|
||||
}
|
||||
public Xoae_app App() {return app;} private final Xoae_app app;
|
||||
public Http_server_wtr Server_wtr() {return server_wtr;} private final Http_server_wtr server_wtr = Http_server_wtr_.New__console();
|
||||
public Http_request_parser Request_parser() {return request_parser;} private final Http_request_parser request_parser;
|
||||
public Gfo_url_encoder Encoder() {return encoder;} private final Gfo_url_encoder encoder = Gfo_url_encoder_.New__http_url().Make();
|
||||
public int Port() {return port;}
|
||||
public Http_server_mgr Port_(int v, boolean caller_is_cfg) {
|
||||
if ( caller_is_cfg
|
||||
&& v == Port__default // new_val == 8080
|
||||
&& port != Port__default) {// cur_val != 8080
|
||||
return this; // exit; do not override command-line value with cfg_value
|
||||
}
|
||||
port = v;
|
||||
return this;
|
||||
} private int port = Port__default;
|
||||
public Http_server_wkr_pool Wkr_pool() {return wkr_pool;} private final Http_server_wkr_pool wkr_pool = new Http_server_wkr_pool();
|
||||
public Int_pool Uid_pool() {return uid_pool;} private final Int_pool uid_pool = new Int_pool();
|
||||
public byte[] Home() {return home;} public void Home_(byte[] v) {home = Bry_.Add(Byte_ascii.Slash_bry, v);} private byte[] home = Bry_.new_a7("/home/wiki/Main_Page");
|
||||
private void Running_(boolean val) {
|
||||
if (val) {
|
||||
if (running)
|
||||
Note("HTTP Server already started");
|
||||
else {
|
||||
Run();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (running) {
|
||||
wkr.Canceled_(true);
|
||||
wkr = null;
|
||||
Note("HTTP Server stopped");
|
||||
}
|
||||
else
|
||||
Note("HTTP Server not started");
|
||||
}
|
||||
running = val;
|
||||
}
|
||||
public void Run() {
|
||||
app.Cfg().Bind_many_app(this, Cfg__port, Cfg__file_retrieve_mode);
|
||||
if (wkr == null) wkr = new Http_server_socket(this);
|
||||
Thread_adp_.Start_by_key("thread:xowa.http_server.server", wkr, Http_server_socket.Invk_run);
|
||||
Note("HTTP Server started: Navigate to http://localhost:" + Int_.To_str(port));
|
||||
}
|
||||
public void Run_xowa_cmd(Xoae_app app, String url_encoded_str) {
|
||||
Gfo_url_encoder url_converter = Gfo_url_encoder_.New__http_url().Make(); // create instance for each call
|
||||
String cmd = url_converter.Decode_str(url_encoded_str);
|
||||
app.Gfs_mgr().Run_str(cmd);
|
||||
}
|
||||
public String Parse_page_to_html(Http_data__client data__client, byte[] wiki_domain, byte[] ttl_bry) {
|
||||
synchronized (thread_lock) {
|
||||
// create a shim gui to automatically handle default XOWA gui JS calls
|
||||
if (init_gui_needed) {
|
||||
init_gui_needed = false;
|
||||
Gxw_html_server.Init_gui_for_server(app, null);
|
||||
}
|
||||
|
||||
// get the wiki
|
||||
Xowe_wiki wiki = (Xowe_wiki)app.Wiki_mgr().Get_by_or_make_init_y(wiki_domain); // assert init for Main_Page; EX:click zh.w on wiki sidebar; DATE:2015-07-19
|
||||
if (Runtime_.Memory_total() > Io_mgr.Len_gb) Xowe_wiki_.Rls_mem(wiki, true); // release memory at 1 GB; DATE:2015-09-11
|
||||
|
||||
// get the url / ttl
|
||||
if (Bry_.Len_eq_0(ttl_bry)) ttl_bry = wiki.Props().Main_page();
|
||||
Xoa_url url = wiki.Utl__url_parser().Parse(ttl_bry);
|
||||
Xoa_ttl ttl = Xoa_ttl.Parse(wiki, url.To_bry_page_w_anch()); // changed from ttl_bry to page_w_anch; DATE:2017-07-24
|
||||
|
||||
// get the page
|
||||
gplx.xowa.guis.views.Xog_tab_itm tab = Gxw_html_server.Assert_tab2(app, wiki); // HACK: assert tab exists
|
||||
Xoae_page page = wiki.Page_mgr().Load_page(url, ttl, tab);
|
||||
app.Gui_mgr().Browser_win().Active_page_(page); // HACK: init gui_mgr's page for output (which server ordinarily doesn't need)
|
||||
if (page.Db().Page().Exists_n()) { // if page does not exist, replace with message; else null_ref error; DATE:2014-03-08
|
||||
page.Db().Text().Text_bry_(Bry_.new_a7("'''Page not found.'''"));
|
||||
wiki.Parser_mgr().Parse(page, false);
|
||||
}
|
||||
page.Html_data().Head_mgr().Itm__server().Init_by_http(data__client).Enabled_y_();
|
||||
|
||||
// generate html
|
||||
String rv = String_.new_u8(wiki.Html_mgr().Page_wtr_mgr().Gen(page, Xopg_page_.Tid_read)); // NOTE: must generate HTML now in order for "wait" and "async_server" to work with text_dbs; DATE:2016-07-10
|
||||
boolean rebuild_html = false;
|
||||
switch (retrieve_mode) {
|
||||
case File_retrieve_mode.Mode_skip: // noop
|
||||
break;
|
||||
case File_retrieve_mode.Mode_async_server:
|
||||
rebuild_html = true;
|
||||
app.Gui_mgr().Browser_win().Page__async__bgn(tab);
|
||||
break;
|
||||
case File_retrieve_mode.Mode_wait:
|
||||
rebuild_html = true;
|
||||
gplx.xowa.guis.views.Xog_async_wkr.Async(page, tab.Html_itm());
|
||||
page = wiki.Page_mgr().Load_page(url, ttl, tab); // HACK: fetch page again so that HTML will now include img data
|
||||
break;
|
||||
}
|
||||
if (rebuild_html) rv = String_.new_u8(wiki.Html_mgr().Page_wtr_mgr().Gen(page, Xopg_page_.Tid_read));
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
private void Note(String s) {
|
||||
// usr_dlg.Prog_many("", "", s); // messages should write to progress bar for gui
|
||||
usr_dlg.Note_many("", "", s);
|
||||
}
|
||||
public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
|
||||
if (ctx.Match(k, Cfg__port)) Port_(m.ReadInt("v"), true);
|
||||
else if (ctx.Match(k, Cfg__file_retrieve_mode)) retrieve_mode = File_retrieve_mode.Xto_byte(m.ReadStr("v"));
|
||||
else if (ctx.Match(k, Invk_running_)) Running_(m.ReadYn("v"));
|
||||
else return Gfo_invk_.Rv_unhandled;
|
||||
return this;
|
||||
}
|
||||
private static final String Invk_running_ = "running_";
|
||||
private static final String
|
||||
Cfg__port = "xowa.addon.http_server.port"
|
||||
, Cfg__file_retrieve_mode = "xowa.addon.http_server.file_retrieve_mode";
|
||||
private static final int Port__default = 8080;
|
||||
}
|
||||
|
||||
@@ -13,3 +13,50 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.http; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import gplx.core.net.*; import gplx.core.threads.*; import gplx.core.primitives.*;
|
||||
class Http_server_socket implements Gfo_invk {
|
||||
private final Http_server_mgr server_mgr;
|
||||
private Server_socket_adp server_socket;
|
||||
public Http_server_socket(Http_server_mgr server_mgr) {this.server_mgr = server_mgr;}
|
||||
public boolean Canceled() {return canceled;}
|
||||
public Http_server_socket Canceled_(boolean v) {
|
||||
canceled = v;
|
||||
if (canceled) {
|
||||
server_socket.Rls();
|
||||
server_socket = null;
|
||||
}
|
||||
return this;
|
||||
} private boolean canceled;
|
||||
public void Run() {
|
||||
if (server_socket == null) server_socket = new Server_socket_adp__base().Ctor(server_mgr.Port());
|
||||
while (true) { // listen for incoming requests
|
||||
Socket_adp client_socket = server_socket.Accept(); // NOTE: blocking call
|
||||
int wkr_uid = -1; // NOTE: default to -1 to handle cases when http_server.max_clients is not set; DATE:2015-10-11
|
||||
Http_server_wkr_pool wkr_pool = server_mgr.Wkr_pool();
|
||||
if (wkr_pool.Enabled()) {
|
||||
Http_server_wtr server_wtr = server_mgr.Server_wtr();
|
||||
int timeout = wkr_pool.Timeout();
|
||||
boolean print_msg = true;
|
||||
while (wkr_pool.Full()) {
|
||||
if (print_msg) {
|
||||
print_msg = false;
|
||||
server_wtr.Write_str_w_nl("maximum # of concurrent connections reached; max=" + wkr_pool.Max() + " timeout=" + timeout);
|
||||
}
|
||||
Thread_adp_.Sleep(timeout);
|
||||
}
|
||||
wkr_uid = server_mgr.Uid_pool().Get_next();
|
||||
wkr_pool.Add(wkr_uid);
|
||||
// server_wtr.Write_str_w_nl("added new worker; uid=" + wkr_uid);
|
||||
}
|
||||
Http_server_wkr wkr = new Http_server_wkr(server_mgr, wkr_uid);
|
||||
wkr.Init_by_thread(client_socket);
|
||||
Thread_adp_.Start_by_key("thread:xowa.http_server.client", wkr, Http_server_wkr.Invk_run);
|
||||
}
|
||||
}
|
||||
public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
|
||||
if (ctx.Match(k, Invk_run)) this.Run();
|
||||
else return Gfo_invk_.Rv_unhandled;
|
||||
return this;
|
||||
} public static final String Invk_run = "run";
|
||||
}
|
||||
|
||||
@@ -13,3 +13,171 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.http; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import gplx.core.ios.*; import gplx.core.ios.streams.*;
|
||||
import gplx.core.primitives.*; import gplx.core.net.*; import gplx.langs.htmls.encoders.*;
|
||||
import gplx.xowa.apps.*;
|
||||
import gplx.xowa.htmls.js.*;
|
||||
class Http_server_wkr implements Gfo_invk {
|
||||
private final int uid;
|
||||
private final Http_server_mgr server_mgr;
|
||||
private final Http_server_wtr server_wtr;
|
||||
private final Http_client_wtr client_wtr = Http_client_wtr_.new_stream();
|
||||
private final Http_client_rdr client_rdr = Http_client_rdr_.new_stream();
|
||||
private final Http_request_parser request_parser;
|
||||
private final Gfo_url_encoder url_encoder;
|
||||
private final Xoae_app app;
|
||||
private final String root_dir_http;
|
||||
private final byte[] root_dir_fsys;
|
||||
private final Bry_bfr tmp_bfr = Bry_bfr_.New_w_size(64);
|
||||
private Socket_adp socket;
|
||||
private Http_data__client data__client;
|
||||
public Http_server_wkr(Http_server_mgr server_mgr, int uid){
|
||||
this.server_mgr = server_mgr; this.uid = uid;
|
||||
this.app = server_mgr.App(); this.server_wtr = server_mgr.Server_wtr(); this.url_encoder = server_mgr.Encoder();
|
||||
this.root_dir_http = app.Fsys_mgr().Root_dir().To_http_file_str();
|
||||
this.root_dir_fsys = Bry_.new_u8(app.Fsys_mgr().Root_dir().Raw());
|
||||
this.request_parser = server_mgr.Request_parser();
|
||||
}
|
||||
public void Init_by_thread(Socket_adp socket) {
|
||||
this.socket = socket;
|
||||
}
|
||||
public void Run(){
|
||||
Http_request_itm request = null;
|
||||
try {
|
||||
client_rdr.Stream_(socket.Get_input_stream());
|
||||
client_wtr.Stream_(socket.Get_output_stream());
|
||||
request = request_parser.Parse(client_rdr);
|
||||
this.data__client = new Http_data__client(request.Host(), socket.Ip_address());
|
||||
byte[] url_bry = request.Url();
|
||||
if (Bry_.Eq(url_bry, Url__home)) url_bry = server_mgr.Home(); // "localhost:8080" comes thru as url of "/"; transform to custom home page; DATE:2015-10-11
|
||||
switch (request.Type()) {
|
||||
case Http_request_itm.Type_get: Process_get(request, url_bry); break;
|
||||
case Http_request_itm.Type_post: Process_post(request); break;
|
||||
}
|
||||
client_wtr.Rls(); // client_rdr.Rls(); socket.Rls();
|
||||
}
|
||||
catch (Exception e) {
|
||||
String request_str = request == null ? "<<NULL>>" : request.To_str(tmp_bfr, Bool_.N);
|
||||
server_wtr.Write_str_w_nl(String_.Format("failed to process request;\nrequest={0}\nerr_msg={1}", request_str, Err_.Message_gplx_full(e)));
|
||||
}
|
||||
finally {
|
||||
if (uid != -1) { // only release if uid was acquired; DATE:2015-10-11
|
||||
server_mgr.Wkr_pool().Del(uid);
|
||||
server_mgr.Uid_pool().Del(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void Process_get(Http_request_itm request, byte[] url) {
|
||||
server_wtr.Write_str_w_nl(String_.new_u8(request.Host()) + "|GET|" + String_.new_u8(request.Url())); // use request url
|
||||
if (Bry_.Has_at_bgn(url, Url__fsys)) Serve_file(url);
|
||||
else if (Bry_.Has_at_bgn(url, Url__exec)) Exec_exec(url, Url__exec);
|
||||
else if (Bry_.Has_at_bgn(url, Url__exec_2)) Exec_exec(url, Url__exec_2);
|
||||
else Write_wiki(url);
|
||||
}
|
||||
private void Serve_file(byte[] url) {
|
||||
tmp_bfr.Clear().Add(root_dir_fsys); // add "C:\xowa\"
|
||||
int question_pos = Bry_find_.Find_fwd(url, Byte_ascii.Question);
|
||||
int url_bgn = Bry_.Has_at_bgn(url, Url__fsys) ? Url__fsys_len : 0; // most files will have "/fsys/" at start, but Mathjax will not
|
||||
int url_end = question_pos == Bry_find_.Not_found ? url.length : question_pos; // ignore files with query params; EX: /file/A.png?key=val
|
||||
url_encoder.Decode(tmp_bfr, Bool_.N, url, url_bgn, url_end); // decode url to actual chars; note that XOWA stores on fsys in UTF-8 chars; "<22>" not "%C3"
|
||||
byte[] path = tmp_bfr.To_bry_and_clear();
|
||||
if (gplx.core.envs.Op_sys.Cur().Tid_is_wnt()) path = Bry_.Replace(path, Byte_ascii.Backslash, Byte_ascii.Slash);
|
||||
client_wtr.Write_bry(Xosrv_http_wkr_.Rsp__http_ok);
|
||||
// client_wtr.Write_str("Expires: Sun, 17-Jan-2038 19:14:07 GMT\n");
|
||||
String mime_type = String_.new_u8(Http_file_utl.To_mime_type_by_path_as_bry(path));
|
||||
client_wtr.Write_str("Content-Type: " + mime_type + "\n\n");
|
||||
Io_stream_rdr file_stream = Io_stream_rdr_.New_by_url(Io_url_.new_fil_(String_.new_u8(path))).Open();
|
||||
client_wtr.Write_stream(file_stream);
|
||||
file_stream.Rls(); client_rdr.Rls(); socket.Rls();
|
||||
}
|
||||
private void Exec_exec(byte[] url, byte[] tkn_bgn) {
|
||||
byte[] cmd = Bry_.Mid(url, tkn_bgn.length);
|
||||
app.Http_server().Run_xowa_cmd(app, String_.new_u8(cmd));
|
||||
}
|
||||
private void Write_wiki(byte[] req) {
|
||||
String wiki_domain = ""; String page_name = "";
|
||||
String[] req_split = String_.Split(String_.new_u8(req), "/");
|
||||
if(req_split.length >= 1){
|
||||
wiki_domain = req_split[1];
|
||||
}
|
||||
if(req_split.length >= 4){
|
||||
page_name = req_split[3];
|
||||
for(int i = 4; i <= req_split.length-1; i++){
|
||||
page_name += "/" + req_split[i];
|
||||
}
|
||||
page_name = url_encoder.Decode_str(page_name);
|
||||
}
|
||||
String page_html = app.Http_server().Parse_page_to_html(data__client, Bry_.new_u8(wiki_domain), Bry_.new_u8(page_name));
|
||||
page_html = Convert_page(page_html, root_dir_http, wiki_domain);
|
||||
Xosrv_http_wkr_.Write_response_as_html(client_wtr, Bool_.N, page_html);
|
||||
}
|
||||
private void Process_post(Http_request_itm request) {
|
||||
byte[] msg = request.Post_data_hash().Get_by(Key__msg).Val();
|
||||
byte[] app_mode = request.Post_data_hash().Get_by(Key__app_mode).Val();
|
||||
Xoa_app_mode app_mode_itm = Xoa_app_mode.parse(String_.new_u8(app_mode));
|
||||
server_wtr.Write_str_w_nl(String_.new_u8(request.Host()) + "|POST|" + String_.new_u8(msg));
|
||||
Object url_tid_obj = post_url_hash.Get_by_bry(request.Url()); if (url_tid_obj == null) throw Err_.new_wo_type("unknown url", "url", request.Url(), "request", request.To_str(tmp_bfr, Bool_.N));
|
||||
String rv = null;
|
||||
switch (((Int_obj_val)url_tid_obj).Val()) {
|
||||
case Tid_post_url_json:
|
||||
rv = app.Html__bridge_mgr().Cmd_mgr().Exec(msg);
|
||||
break;
|
||||
case Tid_post_url_gfs:
|
||||
rv = Object_.Xto_str_strict_or_null_mark(app.Gfs_mgr().Run_str(String_.new_u8(msg)));
|
||||
break;
|
||||
}
|
||||
if (app_mode_itm.Tid_is_http())
|
||||
rv = Convert_page(rv, root_dir_http , "<<MISSING_WIKI>>");
|
||||
Xosrv_http_wkr_.Write_response_as_html(client_wtr, app_mode_itm.Tid() == Xoa_app_mode.Itm_file.Tid(), rv);
|
||||
}
|
||||
private static final byte[] Key__msg = Bry_.new_a7("msg"), Key__app_mode = Bry_.new_a7("app_mode");
|
||||
private static final int Tid_post_url_json = 1, Tid_post_url_gfs = 2;
|
||||
private static final Hash_adp_bry post_url_hash = Hash_adp_bry.ci_a7()
|
||||
.Add_str_int("/exec/json" , Tid_post_url_json)
|
||||
.Add_str_int("/exec/gfs" , Tid_post_url_gfs)
|
||||
;
|
||||
private static String Convert_page(String page_html, String root_dir_http, String wiki_domain) {
|
||||
page_html = String_.Replace(page_html, root_dir_http , "/fsys/");
|
||||
page_html = String_.Replace(page_html, "xowa-cmd:" , "/exec/");
|
||||
page_html = String_.Replace(page_html, "<a href=\"/wiki/" , "<a href=\"/" + wiki_domain + "/wiki/");
|
||||
page_html = String_.Replace(page_html, "<a href='/wiki/" , "<a href='/" + wiki_domain + "/wiki/");
|
||||
page_html = String_.Replace(page_html, "action=\"/wiki/" , "action=\"/" + wiki_domain + "/wiki/");
|
||||
page_html = String_.Replace(page_html, "/site" , "");
|
||||
return page_html;
|
||||
}
|
||||
public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
|
||||
if (ctx.Match(k, Invk_run)) {this.Run();}
|
||||
else return Gfo_invk_.Rv_unhandled;
|
||||
return this;
|
||||
} public static final String Invk_run = "run";
|
||||
private static final byte[]
|
||||
Url__home = Bry_.new_a7("/"), Url__fsys = Bry_.new_a7("/fsys/")
|
||||
, Url__exec = Bry_.new_a7("/exec/"), Url__exec_2 = Bry_.new_a7("/xowa-cmd:")
|
||||
;
|
||||
private static final int Url__fsys_len = Url__fsys.length;
|
||||
}
|
||||
class Xosrv_http_wkr_ {
|
||||
public static void Write_response_as_html(Http_client_wtr client_wtr, boolean cross_domain, String html) {Write_response_as_html(client_wtr, cross_domain, Bry_.new_u8(html));}
|
||||
public static void Write_response_as_html(Http_client_wtr client_wtr, boolean cross_domain, byte[] html) {
|
||||
try{
|
||||
// TODO_OLD: add command-line argument to allow testing from local file
|
||||
// if (cross_domain)
|
||||
// client_wtr.Write_str("Access-Control-Allow-Origin: *\n"); // No 'Access-Control-Allow-Origin' header is present on the requested resource.
|
||||
client_wtr.Write_bry
|
||||
( Bry_.Add
|
||||
( Rsp__http_ok
|
||||
, Rsp__content_type_html
|
||||
, Byte_ascii.Nl_bry
|
||||
, html
|
||||
));
|
||||
} catch (Exception err) {
|
||||
client_wtr.Write_str("Site not found. Check address please, or see console log.\n" + Err_.Message_lang(err));
|
||||
client_wtr.Rls();
|
||||
}
|
||||
}
|
||||
public static final byte[]
|
||||
Rsp__http_ok = Bry_.new_a7("HTTP/1.1 200 OK:\n")
|
||||
, Rsp__content_type_html = Bry_.new_a7("Content-Type: text/html; charset=utf-8\n")
|
||||
;
|
||||
}
|
||||
|
||||
@@ -13,3 +13,25 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.http; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import gplx.xowa.wikis.domains.*; import gplx.xowa.langs.*;
|
||||
class Http_server_wkr_ {
|
||||
public static String Assert_main_page(Xoae_app app, String req) {
|
||||
int mode = -1;
|
||||
String[] req_array = String_.Split(req, "/");
|
||||
if (String_.Has_at_end(req, "wiki/")) mode = 0;
|
||||
else if (String_.Has_at_end(req, "wiki")) mode = 1;
|
||||
else if (req_array.length == 3) mode = 2;
|
||||
if (mode == -1) return req; // not a link to a Main Page; EX:localhost:8080/en.wikipedia.org/wiki/Earth
|
||||
if (req_array.length < 3) return req; // shouldn't happen; EX: "localhost:8080wiki"
|
||||
byte[] wiki_domain = Bry_.new_u8(req_array[1]);
|
||||
Xow_domain_itm domain_itm = Xow_domain_itm_.parse(wiki_domain);
|
||||
if (domain_itm.Domain_type_id() == Xow_domain_tid_.Tid__other && domain_itm.Lang_actl_itm().Id() == Xol_lang_stub_.Id__intl) return req;
|
||||
Xowe_wiki wiki = app.Wiki_mgr().Get_by_or_make(wiki_domain);
|
||||
wiki.Init_assert();
|
||||
String main_page = String_.new_u8(wiki.Props().Main_page());
|
||||
if (mode == 1) main_page = "/" + main_page;
|
||||
else if (mode == 2) main_page = "wiki/" + main_page;
|
||||
return req + main_page;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,28 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.http; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import org.junit.*;
|
||||
public class Http_server_wkr__tst {
|
||||
@Before public void init() {fxt.Clear();} private Http_server_wkr__fxt fxt = new Http_server_wkr__fxt();
|
||||
@Test public void Assert_main_page() {
|
||||
fxt.Init_wiki_main_page("fr.wikiversity.org", "Accueil");
|
||||
fxt.Test_assert_main_page("/fr.wikiversity.org/" , "/fr.wikiversity.org/wiki/Accueil");
|
||||
fxt.Test_assert_main_page("/fr.wikiversity.org/wiki" , "/fr.wikiversity.org/wiki/Accueil");
|
||||
fxt.Test_assert_main_page("/fr.wikiversity.org/wiki/" , "/fr.wikiversity.org/wiki/Accueil");
|
||||
fxt.Test_assert_main_page("/fr.wikiversity.org/wiki/A" , "/fr.wikiversity.org/wiki/A");
|
||||
}
|
||||
}
|
||||
class Http_server_wkr__fxt {
|
||||
private Xoae_app app;
|
||||
public void Clear() {
|
||||
this.app = Xoa_app_fxt.Make__app__edit();
|
||||
}
|
||||
public void Init_wiki_main_page(String domain, String main_page) {
|
||||
Xowe_wiki wiki = app.Wiki_mgr().Get_by_or_make(Bry_.new_u8(domain));
|
||||
wiki.Props().Main_page_(Bry_.new_u8(main_page));
|
||||
}
|
||||
public void Test_assert_main_page(String url, String expd) {
|
||||
Tfds.Eq(expd, Http_server_wkr_.Assert_main_page(app, url));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,29 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.http; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import gplx.core.primitives.*;
|
||||
public class Http_server_wkr_pool {
|
||||
private final Ordered_hash hash = Ordered_hash_.New(); private final Int_obj_ref hash_key = Int_obj_ref.New_neg1();
|
||||
public boolean Enabled() {return max != 0;}
|
||||
public int Max() {return max;} private int max;
|
||||
public int Timeout() {return timeout;} private int timeout;
|
||||
public int Len() {return len;} private int len;
|
||||
public boolean Full() {return len >= max;}
|
||||
public void Init(int max, int timeout) {this.max = max; this.timeout = timeout;}
|
||||
public void Add(int uid) {
|
||||
if (max == 0) return; // disabled
|
||||
synchronized (hash) {
|
||||
Int_obj_ref wkr_key = Int_obj_ref.New(uid);
|
||||
hash.Add(wkr_key, wkr_key);
|
||||
++len;
|
||||
}
|
||||
}
|
||||
public void Del(int uid) {
|
||||
if (max == 0) return; // disabled
|
||||
synchronized (hash) {
|
||||
hash.Del(hash_key.Val_(uid));
|
||||
--len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,41 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.tcp; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import gplx.core.ios.*; import gplx.core.ios.streams.*;
|
||||
public class Socket_rdr {
|
||||
private java.net.ServerSocket server;
|
||||
private java.net.Socket client;
|
||||
private java.io.InputStream stream;
|
||||
public IoStream Rdr_stream() {return rdr_stream;} private IoStream rdr_stream = null;
|
||||
public int Port() {return port;} private int port;
|
||||
public Socket_rdr Ctor(int port) {this.port = port; return this;}
|
||||
public Socket_rdr Open() {
|
||||
try {
|
||||
// this.Rls();
|
||||
if (server == null) {
|
||||
server = new java.net.ServerSocket(port);
|
||||
server.setReuseAddress(true);
|
||||
}
|
||||
client = server.accept();
|
||||
client.setSoTimeout(10000);
|
||||
stream = client.getInputStream();
|
||||
rdr_stream = new IoStream_stream_rdr().UnderRdr_(stream);
|
||||
return this;
|
||||
} catch (Exception e) {throw Err_.new_exc(e, "net", "failed to open socket", "port", port);}
|
||||
}
|
||||
public void Close() {
|
||||
try {
|
||||
// if (server != null) server.close();
|
||||
if (client != null) client.close();
|
||||
if (stream != null) stream.close();
|
||||
} catch (Exception e) {throw Err_.new_exc(e, "net", "failed to close socket", "port", port);}
|
||||
}
|
||||
public void Rls() {
|
||||
try {
|
||||
if (server != null) server.close();
|
||||
if (client != null) client.close();
|
||||
if (stream != null) stream.close();
|
||||
} catch (Exception e) {throw Err_.new_exc(e, "net", "failed to rls socket", "port", port);}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,37 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.tcp; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
public class Socket_wtr {
|
||||
private String host;
|
||||
private int port;
|
||||
private java.net.Socket socket;
|
||||
private java.io.OutputStream stream;
|
||||
public Socket_wtr Ctor(String host, int port) {this.host = host; this.port = port; return this;}
|
||||
public Socket_wtr Open() {
|
||||
try {
|
||||
this.Rls();
|
||||
socket = new java.net.Socket(host, port);
|
||||
socket.setSoTimeout(10000);
|
||||
stream = socket.getOutputStream();
|
||||
return this;
|
||||
} catch (Exception e) {throw Err_.new_exc(e, "net", "failed to open socket", "host", host, "port", port);}
|
||||
}
|
||||
public void Write(byte[] bry) {
|
||||
try {
|
||||
stream.write(bry, 0, bry.length);
|
||||
} catch (Exception e) {throw Err_.new_exc(e, "net", "failed to write stream", "host", host, "port", port);}
|
||||
}
|
||||
public void Close() {
|
||||
try {
|
||||
if (stream != null) stream.close();
|
||||
if (socket != null) socket.close();
|
||||
} catch (Exception e) {throw Err_.new_exc(e, "net", "failed to close socket", "host", host, "port", port);}
|
||||
}
|
||||
public void Rls() {
|
||||
try {
|
||||
if (stream != null) stream.close();
|
||||
if (socket != null) socket.close();
|
||||
} catch (Exception e) {throw Err_.new_exc(e, "net", "failed to release socket", "host", host, "port", port);}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,11 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.tcp; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
public class Xosrv_cmd_types {
|
||||
public static final byte[]
|
||||
Cmd_exec = Bry_.new_a7("xowa.cmd.exec") , Cmd_pass = Bry_.new_a7("xowa.cmd.result") , Cmd_fail = Bry_.new_a7("xowa.cmd.error")
|
||||
, Js_exec = Bry_.new_a7("xowa.js.exec") , Js_pass = Bry_.new_a7("xowa.js.result") , Js_fail = Bry_.new_a7("xowa.js.error")
|
||||
, Browser_exec = Bry_.new_a7("browser.js.exec"), Browser_pass = Bry_.new_a7("browser.js.result") , Browser_fail = Bry_.new_a7("browser.js.error")
|
||||
;
|
||||
}
|
||||
|
||||
@@ -13,3 +13,107 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.tcp; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
public class Xosrv_msg {
|
||||
public byte Version_tid() {return Version_tid_0;} static final byte Version_tid_0 = 0;
|
||||
public byte[] Cmd_name() {return cmd_name;} private byte[] cmd_name;
|
||||
public byte[] Msg_id() {return msg_id;} private byte[] msg_id;
|
||||
public byte[] Sender() {return sender;} private byte[] sender;
|
||||
public byte[] Recipient() {return recipient;} private byte[] recipient;
|
||||
public byte[] Msg_date() {return msg_date;} private byte[] msg_date;
|
||||
public byte[] Msg_text() {return msg_text;} private byte[] msg_text;
|
||||
public void Print(Bry_bfr bfr) {
|
||||
int body_len = cmd_name.length + msg_id.length + sender.length + recipient.length + msg_date.length + msg_text.length + 5; // 5=5 pipes for 6 fields
|
||||
int cksum = (body_len * 2) + 1;
|
||||
bfr.Add_int_fixed(this.Version_tid() , 1).Add_byte_pipe(); // 0|
|
||||
bfr.Add_int_fixed(body_len , 10).Add_byte_pipe(); // 0123456789|
|
||||
bfr.Add_int_fixed(cksum , 10).Add_byte_pipe(); // 0123456789|
|
||||
bfr.Add(cmd_name ).Add_byte_pipe(); // cmd|
|
||||
bfr.Add(msg_id ).Add_byte_pipe(); // id|
|
||||
bfr.Add(sender ).Add_byte_pipe(); // sender|
|
||||
bfr.Add(recipient ).Add_byte_pipe(); // recipient|
|
||||
bfr.Add(msg_date ).Add_byte_pipe(); // msg_date|
|
||||
bfr.Add(msg_text ); // msg_text
|
||||
}
|
||||
public static final Xosrv_msg Exit = new Xosrv_msg();
|
||||
public static Xosrv_msg fail_(String fmt, Object... ary) {
|
||||
Xosrv_msg rv = new Xosrv_msg();
|
||||
rv.msg_text = Bry_.new_u8(String_.Format(fmt, ary));
|
||||
return rv;
|
||||
}
|
||||
public static Xosrv_msg new_(byte[] cmd_name, byte[] msg_id, byte[] sender, byte[] recipient, byte[] msg_date, byte[] msg_text) {
|
||||
Xosrv_msg rv = new Xosrv_msg();
|
||||
rv.cmd_name = cmd_name;
|
||||
rv.msg_id = msg_id;
|
||||
rv.sender = sender;
|
||||
rv.recipient = recipient;
|
||||
rv.msg_date = msg_date;
|
||||
rv.msg_text = msg_text;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
/*
|
||||
Message definition
|
||||
Id : 0
|
||||
Purpose : Version number for message format
|
||||
Data type : int
|
||||
Notes : Always 0; will change to 1 if message format ever changes
|
||||
Example : "0"
|
||||
|
||||
Id : 1
|
||||
Description : Body length; specified total length of message field 3 (body)
|
||||
Data type : int
|
||||
Notes : always zero-padded to 10 bytes (not hexadecimal)
|
||||
Example : "0000000123"
|
||||
|
||||
Id : 2
|
||||
Description : Checksum; should equal (2 * body length) + 1
|
||||
Data type : int
|
||||
Notes : always zero-padded to 10 bytes (not hexadecimal)
|
||||
Example : "0000000247"
|
||||
|
||||
Id : 3
|
||||
Description : Body
|
||||
Data type : String
|
||||
Notes : length specified by field 1 (body length)
|
||||
Example : see below
|
||||
|
||||
Body definition
|
||||
* Pipes are not allowed in any field except for the last
|
||||
* Only the first field is required
|
||||
|
||||
Id : 0
|
||||
Purpose : Command name
|
||||
Data type : String
|
||||
Notes : unique name identifying the command
|
||||
Example : "xowa.cmd.exec", "xowa.cmd.result", "xowa.cmd.error", "xowa.js.exec", "xowa.js.result", "xowa.js.error"
|
||||
|
||||
Id : 1
|
||||
Purpose : Message id
|
||||
Data type : String
|
||||
Notes : Usage is defined by callers; can be empty
|
||||
Example : "1", ""
|
||||
|
||||
Id : 2
|
||||
Purpose : Sender id
|
||||
Data type : String
|
||||
Notes : Usage is defined by callers; can be empty
|
||||
Example : "tab1", "xowa", ""
|
||||
|
||||
Id : 3
|
||||
Purpose : Recipient id
|
||||
Data type : String
|
||||
Notes : Usage is defined by callers; can be empty
|
||||
Example : "xowa", "tab1", ""
|
||||
|
||||
Id : 4
|
||||
Purpose : Message date
|
||||
Data type : String
|
||||
Notes : ISO 8601 format; see http://www.w3.org/TR/NOTE-datetime; Usage is defined by callers; can be empty
|
||||
Example : "1997-07-16T19:20:30.45+01:00", ""
|
||||
|
||||
Id : 5
|
||||
Purpose : Message text
|
||||
Data type : String
|
||||
Notes : freeform; can contain any character
|
||||
Example : "app.shell.fetch_page('simple.wikipedia.org/wiki/Earth', 'html');"*/
|
||||
|
||||
@@ -13,3 +13,50 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.tcp; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import gplx.core.primitives.*;
|
||||
import gplx.core.ios.*; import gplx.core.ios.streams.*; import gplx.core.texts.*;
|
||||
public class Xosrv_msg_rdr {
|
||||
public Xosrv_msg_rdr(byte[] default_body_bry, IoStream rdr) {this.default_body_bry = default_body_bry; default_body_bry_len = default_body_bry.length; this.rdr = rdr;} private byte[] header_bry = new byte[24], default_body_bry; int default_body_bry_len;
|
||||
public IoStream Rdr() {return rdr;} private IoStream rdr;
|
||||
public Xosrv_msg Read() {
|
||||
int bytes_read = rdr.Read(header_bry, 0, 24); // 24 = version(1) + pipe + msg_len (10) + pipe + cksum (10) + pipe
|
||||
if (bytes_read < 24) {
|
||||
if (bytes_read == -1) return Xosrv_msg.Exit; // stream closed; should only occur when shutting down
|
||||
else return Xosrv_msg.fail_("header is invalid; hdr:{0}", String_.new_u8(header_bry, 0, bytes_read));
|
||||
}
|
||||
byte version = header_bry[0]; if (version != Byte_ascii.Num_0) return Xosrv_msg.fail_("version must be 0; version:{0}", Byte_.To_str(version));
|
||||
int body_len = Bry_.To_int_or(header_bry, 2, 12, -1); if (body_len == -1) return Xosrv_msg.fail_("body_len is not number; body_len:{0}", String_.new_u8(header_bry, 2, 23));
|
||||
int cksum = Bry_.To_int_or(header_bry, 13, 23, -1); if (cksum == -1) return Xosrv_msg.fail_("checksum is not number; cksum:{0}", String_.new_u8(header_bry, 13, 23));
|
||||
if (!Chk_bytes(header_bry, Byte_ascii.Pipe, 1, 12, 23)) return Xosrv_msg.fail_("message should be delimited by pipes at 1, 12 and 23; message:{0}", String_.new_u8(header_bry, 0, 24));
|
||||
if (cksum != (body_len * 2) + 1) return Xosrv_msg.fail_("checksum failed; body_len:{0} chksum:{1}", body_len, cksum);
|
||||
byte[] body_bry = body_len > default_body_bry_len ? new byte[body_len] : default_body_bry;
|
||||
rdr.Read(body_bry, 0, body_len);
|
||||
Int_obj_ref fld_bgn = Int_obj_ref.New_zero(); Bool_obj_ref fail_ref = Bool_obj_ref.n_(); String_obj_ref fld_ref = String_obj_ref.null_();
|
||||
byte[] cmd_name = Read_fld(body_bry, body_len, fld_bgn, fail_ref, fld_ref.Val_("cmd_name")); if (fail_ref.Val()) return Read_fld_fail(fld_ref, body_bry);
|
||||
byte[] msg_id = Read_fld(body_bry, body_len, fld_bgn, fail_ref, fld_ref.Val_("msg_id")); if (fail_ref.Val()) return Read_fld_fail(fld_ref, body_bry);
|
||||
byte[] sender = Read_fld(body_bry, body_len, fld_bgn, fail_ref, fld_ref.Val_("sender")); if (fail_ref.Val()) return Read_fld_fail(fld_ref, body_bry);
|
||||
byte[] recipient = Read_fld(body_bry, body_len, fld_bgn, fail_ref, fld_ref.Val_("recipient")); if (fail_ref.Val()) return Read_fld_fail(fld_ref, body_bry);
|
||||
byte[] msg_date = Read_fld(body_bry, body_len, fld_bgn, fail_ref, fld_ref.Val_("msg_date")); if (fail_ref.Val()) return Read_fld_fail(fld_ref, body_bry);
|
||||
byte[] msg_text = Bry_.Mid(body_bry, fld_bgn.Val(), body_len);
|
||||
return Xosrv_msg.new_(cmd_name, msg_id, sender, recipient, msg_date, msg_text);
|
||||
}
|
||||
private static byte[] Read_fld(byte[] bry, int bry_len, Int_obj_ref fld_bgn, Bool_obj_ref fail_ref, String_obj_ref fld_ref) {
|
||||
int fld_end = Bry_find_.Find_fwd(bry, Byte_ascii.Pipe, fld_bgn.Val(), bry_len);
|
||||
if (fld_end == Bry_find_.Not_found) {fail_ref.Val_y_(); return null;}
|
||||
byte[] rv = Bry_.Mid(bry, fld_bgn.Val(), fld_end);
|
||||
fld_bgn.Val_(fld_end + 1); // +1 to place after pipe
|
||||
return rv;
|
||||
}
|
||||
private static Xosrv_msg Read_fld_fail(String_obj_ref fld_name, byte[] body_bry) {return Xosrv_msg.fail_("pipe not found for " + fld_name.Val() + "; body:{0}", String_.new_u8(body_bry));}
|
||||
private static boolean Chk_bytes(byte[] bry, byte expd, int... pos_ary) {
|
||||
int len = pos_ary.length;
|
||||
int bry_len = bry.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
int pos = pos_ary[i];
|
||||
if (pos >= bry_len) return false; // out of bounds; return false (don't fail)
|
||||
if (bry[pos] != expd) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,51 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.tcp; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import org.junit.*;
|
||||
import gplx.core.ios.*; import gplx.core.ios.streams.*;
|
||||
public class Xosrv_msg_rdr_tst {
|
||||
@Before public void init() {fxt.Clear();} private Xosrv_msg_rdr_fxt fxt = new Xosrv_msg_rdr_fxt();
|
||||
@Test public void Parse() {
|
||||
String raw = "0|0000000045|0000000091|cmd_0|id_0|sender_0|recipient_0|date_0|text_0";
|
||||
Xosrv_msg msg = fxt.Test_parse_msg(raw, "cmd_0", "id_0", "sender_0", "recipient_0", "date_0", "text_0");
|
||||
fxt.Test_print(msg, raw);
|
||||
}
|
||||
@Test public void Err_header_is_invalid() {fxt.Test_parse_err("abcde", "header is invalid");}
|
||||
@Test public void Err_checksum_failed() {fxt.Test_parse_err("0|0000000000|0000000000|", "checksum failed");}
|
||||
@Test public void Err_cmd_missing() {fxt.Test_parse_err("0|0000000001|0000000003|a", "pipe not found for cmd_name");}
|
||||
}
|
||||
class Xosrv_msg_rdr_fxt {
|
||||
public Xosrv_msg_rdr_fxt Clear() {
|
||||
if (msg_rdr == null) {
|
||||
msg_rdr_stream = new IoStream_mock();
|
||||
msg_rdr = new Xosrv_msg_rdr(Bry_.Empty, msg_rdr_stream);
|
||||
}
|
||||
msg_rdr_stream.Reset();
|
||||
return this;
|
||||
} private Xosrv_msg_rdr msg_rdr; private IoStream_mock msg_rdr_stream;
|
||||
public Xosrv_msg Test_parse_msg(String raw, String expd_cmd, String expd_id, String expd_sender, String expd_recipient, String expd_date, String expd_text) {
|
||||
byte[] raw_bry = Bry_.new_a7(raw);
|
||||
msg_rdr_stream.Data_bry_(raw_bry).Read_limit_(raw_bry.length);
|
||||
Xosrv_msg msg = msg_rdr.Read();
|
||||
Tfds.Eq(String_.new_a7(msg.Cmd_name()) , expd_cmd);
|
||||
Tfds.Eq(String_.new_a7(msg.Msg_id()) , expd_id);
|
||||
Tfds.Eq(String_.new_a7(msg.Sender()) , expd_sender);
|
||||
Tfds.Eq(String_.new_a7(msg.Recipient()) , expd_recipient);
|
||||
Tfds.Eq(String_.new_a7(msg.Msg_date()) , expd_date);
|
||||
Tfds.Eq(String_.new_a7(msg.Msg_text()) , expd_text);
|
||||
return msg;
|
||||
}
|
||||
public void Test_parse_err(String raw, String expd_err) {
|
||||
byte[] raw_bry = Bry_.new_a7(raw);
|
||||
msg_rdr_stream.Data_bry_(raw_bry).Read_limit_(raw_bry.length);
|
||||
Xosrv_msg msg = msg_rdr.Read();
|
||||
String msg_text = String_.new_a7(msg.Msg_text());
|
||||
Tfds.Eq_true(String_.Has_at_bgn(msg_text, expd_err), msg_text);
|
||||
}
|
||||
public void Test_print(Xosrv_msg msg, String expd) {
|
||||
Bry_bfr bfr = Bry_bfr_.New();
|
||||
msg.Print(bfr);
|
||||
Tfds.Eq(expd, bfr.To_str_and_clear());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,116 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.tcp; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import gplx.core.primitives.*; import gplx.core.ios.*; import gplx.core.envs.*; import gplx.core.threads.*;
|
||||
import gplx.gfui.controls.standards.*;
|
||||
import gplx.langs.jsons.*;
|
||||
public class Xosrv_server implements Gfo_invk {
|
||||
private long last_cmd;
|
||||
public Xosrv_socket_rdr Rdr() {return rdr;} private Xosrv_socket_rdr rdr = new Xosrv_socket_rdr();
|
||||
public Xosrv_socket_wtr Wtr() {return wtr;} private Xosrv_socket_wtr wtr = new Xosrv_socket_wtr();
|
||||
public int Rdr_port() {return rdr_port;} public Xosrv_server Rdr_port_(int v) {rdr_port = v; return this;} private int rdr_port = 55000;
|
||||
public int Wtr_port() {return wtr_port;} public Xosrv_server Wtr_port_(int v) {wtr_port = v; return this;} private int wtr_port = 55001;
|
||||
public int Shutdown_interval() {return shutdown_interval;} public Xosrv_server Shutdown_interval_(int v) {shutdown_interval = v; return this;} private int shutdown_interval = -1;
|
||||
public String Wtr_host() {return wtr_host;} private String wtr_host = "localhost";
|
||||
public boolean Running() {return running;} public Xosrv_server Running_(boolean v) {running = v; running_str = Bool_.To_str_lower(running); return this;} private boolean running = false;
|
||||
public String Running_str() {return running_str;} String running_str = "false";
|
||||
public void App_ctor(Xoae_app app) {this.app = app;}
|
||||
public Xoae_app App() {return app;} private Xoae_app app;
|
||||
public void Run() {
|
||||
rdr.Init(this, rdr_port);
|
||||
wtr.Init(wtr_host, wtr_port);
|
||||
Gxw_html_server.Init_gui_for_server(app, wtr);
|
||||
Thread_adp_.Start_by_key(gplx.xowa.apps.Xoa_thread_.Key_http_server_main, rdr, Xosrv_socket_rdr.Invk_start);
|
||||
app.Usr_dlg().Note_many("", "", "server started: listening on ~{0}. Press Ctrl+C to exit", rdr_port);
|
||||
last_cmd = System_.Ticks();
|
||||
Running_(true);
|
||||
while (running) {
|
||||
if (shutdown_interval != -1 && System_.Ticks() - last_cmd > shutdown_interval) break;
|
||||
Thread_adp_.Sleep(1000);
|
||||
}
|
||||
rdr.Rls();
|
||||
wtr.Rls();
|
||||
app.Usr_dlg().Note_many("", "", "server stopped", rdr_port);
|
||||
}
|
||||
public void Msg_rcvd(Xosrv_msg msg) {
|
||||
try {
|
||||
byte[] cmd_name = msg.Cmd_name();
|
||||
byte[] rsp_name = Bry_.Empty;
|
||||
long time_bgn = System_.Ticks();
|
||||
last_cmd = time_bgn;
|
||||
byte[] msg_bry = msg.Msg_text();
|
||||
String msg_str = String_.new_u8(msg_bry);
|
||||
app.Usr_dlg().Note_many("", "", "processing cmd: ~{0}", msg_str);
|
||||
String rsp_str = null;
|
||||
if (Bry_.Eq(cmd_name, Xosrv_cmd_types.Cmd_exec)) {rsp_name = Xosrv_cmd_types.Cmd_pass; rsp_str = Exec_cmd(msg_str);}
|
||||
else if (Bry_.Eq(cmd_name, Xosrv_cmd_types.Js_exec)) {rsp_name = Xosrv_cmd_types.Js_pass; rsp_str = Exec_js(msg.Sender(), msg_bry);}
|
||||
Xosrv_msg rsp_msg = Xosrv_msg.new_(rsp_name, msg.Msg_id(), msg.Recipient(), msg.Sender(), msg.Msg_date(), Bry_.new_u8(rsp_str));
|
||||
app.Usr_dlg().Note_many("", "", "sending rsp: bytes=~{0}", String_.Len(rsp_str));
|
||||
wtr.Write(rsp_msg);
|
||||
app.Usr_dlg().Note_many("", "", "rsp sent: elapsed=~{0}", Time_span_.fracs_(System_.Ticks() - time_bgn).XtoStrUiAbbrv());
|
||||
} catch (Exception e) {app.Usr_dlg().Warn_many("", "", "server error: ~{0}", Err_.Message_gplx_full(e));}
|
||||
}
|
||||
private String Exec_cmd(String msg_text) {
|
||||
Object rv_obj = app.Gfs_mgr().Run_str(msg_text);
|
||||
String rv = Type_.Eq_by_obj(rv_obj, String_.Cls_ref_type) ? (String)rv_obj : Object_.Xto_str_strict_or_null(rv_obj);
|
||||
return rv;
|
||||
}
|
||||
public String Exec_js(byte[] sender, byte[] msg_text) {
|
||||
String_obj_ref trace = String_obj_ref.new_("exec_js");
|
||||
try {
|
||||
Object[] xowa_exec_args = xowa_exec_parser.Parse_xowa_exec(msg_text);
|
||||
trace.Val_("js_args");
|
||||
// xowa_exec_args = (Object[])Array_.Resize(xowa_exec_args, xowa_exec_args.length + 1);
|
||||
// xowa_exec_args[xowa_exec_args.length - 1] = sender;
|
||||
Object rv_obj = Gfui_html.Js_args_exec(app.Gui_mgr().Browser_win().Active_html_itm().Js_cbk(), xowa_exec_args);
|
||||
trace.Val_("json_write: " + Object_.Xto_str_strict_or_null_mark(rv_obj));
|
||||
return json_wtr.Write_root(Bry_xowa_js_result, rv_obj).Bld_as_str();
|
||||
} catch (Exception e) {throw Err_.new_exc(e, "http", "exec_js error", "trace", trace, "msg", msg_text);}
|
||||
} private Xosrv_xowa_exec_parser xowa_exec_parser = new Xosrv_xowa_exec_parser(); private Json_doc_srl json_wtr = new Json_doc_srl(); private static final byte[] Bry_xowa_js_result = Bry_.new_a7("xowa_js_result");
|
||||
public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
|
||||
if (ctx.Match(k, Invk_rdr_port)) return rdr_port;
|
||||
else if (ctx.Match(k, Invk_rdr_port_)) rdr_port = m.ReadInt("v");
|
||||
else if (ctx.Match(k, Invk_wtr_port)) return wtr_port;
|
||||
else if (ctx.Match(k, Invk_wtr_port_)) wtr_port = m.ReadInt("v");
|
||||
else if (ctx.Match(k, Invk_wtr_host)) return wtr_host;
|
||||
else if (ctx.Match(k, Invk_wtr_host_)) wtr_host = m.ReadStr("v");
|
||||
else if (ctx.Match(k, Invk_shutdown_interval)) return shutdown_interval;
|
||||
else if (ctx.Match(k, Invk_shutdown_interval_)) shutdown_interval = m.ReadInt("v");
|
||||
else if (ctx.Match(k, Invk_stop)) running = false;
|
||||
else return Gfo_invk_.Rv_unhandled;
|
||||
return this;
|
||||
}
|
||||
public static final String Invk_stop = "stop", Invk_rdr_port = "rdr_port", Invk_rdr_port_ = "rdr_port_", Invk_wtr_port = "wtr_port", Invk_wtr_port_ = "wtr_port_", Invk_wtr_host = "wtr_host", Invk_wtr_host_ = "wtr_host_"
|
||||
, Invk_shutdown_interval = "shutdown_interval", Invk_shutdown_interval_ = "shutdown_interval_";
|
||||
}
|
||||
class Xosrv_xowa_exec_parser {
|
||||
private Json_parser json_parser = new Json_parser();
|
||||
public Object[] Parse_xowa_exec(byte[] msg_text) { // parses JSON with format '{"args":["arg0","arg1","arg2"]}'
|
||||
Json_doc doc = json_parser.Parse(msg_text);
|
||||
Json_kv args_kv = (Json_kv)doc.Root_nde().Get_at(0); // get "args" kv
|
||||
Json_ary args_ary = (Json_ary)args_kv.Val(); // get []
|
||||
int len = args_ary.Len();
|
||||
Object[] rv = new Object[len];
|
||||
for (int i = 0; i < len; i++) { // extract args
|
||||
Json_itm itm = args_ary.Get_at(i);
|
||||
rv[i] = Parse_ary_itm(itm);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
private Object Parse_ary_itm(Json_itm itm) {
|
||||
switch (itm.Tid()) {
|
||||
case Json_itm_.Tid__str:
|
||||
return String_.new_u8(itm.Data_bry());
|
||||
case Json_itm_.Tid__ary:
|
||||
Json_ary ary = (Json_ary)itm;
|
||||
int len = ary.Len();
|
||||
String[] rv = new String[len];
|
||||
for (int i = 0; i < len; i++)
|
||||
rv[i] = String_.new_u8(ary.Get_at(i).Data_bry());
|
||||
return rv;
|
||||
default:
|
||||
throw Err_.new_unhandled(itm.Tid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,26 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.tcp; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import org.junit.*;
|
||||
import gplx.core.ios.*;
|
||||
public class Xosrv_server_tst {
|
||||
@Before public void init() {fxt.Clear();} private Xosrv_server_fxt fxt = new Xosrv_server_fxt();
|
||||
@Test public void Exec_js() {
|
||||
fxt.Test_exec_js("{\"args\":[\"xowa_exec_test\",\"a\",\"b\"]}", "{\"xowa_js_result\":\"xowa_exec_test|a|b\"}");
|
||||
}
|
||||
@Test public void Exec_js_ary() {
|
||||
fxt.Test_exec_js("{\"args\":[\"xowa_exec_test_as_array\",\"a\",\"b\"]}", "{\"xowa_js_result\":[\"xowa_exec_test_as_array\",\"a\",\"b\"]}");
|
||||
}
|
||||
}
|
||||
class Xosrv_server_fxt {
|
||||
public Xosrv_server_fxt Clear() {
|
||||
app = Xoa_app_fxt.Make__app__edit();
|
||||
Xoa_app_fxt.Init_gui(app, null); // NOTE: null wiki does not matter for test
|
||||
return this;
|
||||
} private Xoae_app app;
|
||||
public void Test_exec_js(String raw, String expd) {
|
||||
String actl = app.Tcp_server().Exec_js(null, Bry_.new_a7(raw));
|
||||
Tfds.Eq(expd, actl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,34 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.tcp; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import gplx.core.ios.*; import gplx.core.ios.streams.*;
|
||||
public class Xosrv_socket_rdr implements Gfo_invk {
|
||||
private Socket_rdr rdr = new Socket_rdr();
|
||||
public int Port() {return port;} private int port;
|
||||
public void Init(Xosrv_server server, int port) {this.server = server; this.port = port;} private Xosrv_server server;
|
||||
public void Start() {
|
||||
rdr = new Socket_rdr();
|
||||
try {
|
||||
rdr.Ctor(port);
|
||||
while (true) {
|
||||
rdr.Open();
|
||||
IoStream rdr_stream = rdr.Rdr_stream();
|
||||
Xosrv_msg_rdr msg_rdr = new Xosrv_msg_rdr(new byte[24], rdr_stream);
|
||||
Xosrv_msg msg = msg_rdr.Read();
|
||||
if (msg == Xosrv_msg.Exit) continue;
|
||||
server.Msg_rcvd(msg);
|
||||
rdr.Close();
|
||||
}
|
||||
} catch (Exception e) {Err_.Noop(e);}
|
||||
finally {rdr.Rls();}
|
||||
}
|
||||
public void Rls() {
|
||||
rdr.Rls();
|
||||
}
|
||||
public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
|
||||
if (ctx.Match(k, Invk_start)) this.Start();
|
||||
else return Gfo_invk_.Rv_unhandled;
|
||||
return this;
|
||||
} public static final String Invk_start = "start";
|
||||
}
|
||||
|
||||
@@ -13,3 +13,20 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.tcp; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
public class Xosrv_socket_wtr {
|
||||
public String Host() {return host;} private String host = "localhost";
|
||||
public int Port() {return port;} private int port;
|
||||
private Socket_wtr wtr; private Bry_bfr msg_bfr = Bry_bfr_.Reset(4 * Io_mgr.Len_kb);
|
||||
public void Init(String host, int port) {this.host = host; this.port = port; wtr = new Socket_wtr().Ctor(host, port);}
|
||||
public void Write(Xosrv_msg msg) {
|
||||
wtr.Open();
|
||||
msg.Print(msg_bfr);
|
||||
byte[] msg_bry = msg_bfr.To_bry_and_clear();
|
||||
wtr.Write(msg_bry);
|
||||
wtr.Close();
|
||||
}
|
||||
public void Rls() {
|
||||
wtr.Rls();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,216 @@ The terms of each license can be found in the source code repository:
|
||||
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
|
||||
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
|
||||
*/
|
||||
package gplx.xowa.apps.servers.tcp; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.apps.servers.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
public class Xowa_tcp_console {
|
||||
public static void main(String[] args) {
|
||||
Xowa_tcp_console console = new Xowa_tcp_console();
|
||||
console.Run(args);
|
||||
}
|
||||
private int server_send_port;
|
||||
private int server_recv_port;
|
||||
private String wiki_domain;
|
||||
private int max_length;
|
||||
private Xowa_tcp_sender sender;
|
||||
public void Run(String[] args) {
|
||||
Print_message_line("XOWA TCP client v0.0.0.0");
|
||||
|
||||
// parse args
|
||||
if (!Parse_args(args)) {
|
||||
Print_message_line("XOWA console requires 4 args: server_send_port, server_recv_port, wiki_domain, max_length.");
|
||||
Print_message_line("For example, use '55000 55001 simple.wikipedia.org 1000'");
|
||||
return;
|
||||
}
|
||||
|
||||
// start sender
|
||||
sender = new Xowa_tcp_sender(server_send_port);
|
||||
|
||||
// start console
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
|
||||
Print_message_line("Enter page name. For example 'Earth'. Press Ctrl+C to exit. Enter '|server_stop' to stop server or '|exit' to exit.");
|
||||
Print_message("> ");
|
||||
try {
|
||||
while (true) {
|
||||
String page = Read_string(input);
|
||||
if (page == null)
|
||||
break;
|
||||
else if ("|exit".equals(page))
|
||||
break;
|
||||
else if ("|server_stop".equals(page)) {
|
||||
Send_message_server_stop();
|
||||
}
|
||||
else
|
||||
Send_message_fetch_page(page);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
Print_error(e);
|
||||
}
|
||||
}
|
||||
private void Send_message_fetch_page(String page) {
|
||||
Print_message_line("Sending request for " + page);
|
||||
// String xowa_msg = "0|0000000128|0000000257|xowa.cmd.exec|id_0|sender_0|recipient_0|2013-07-18 01:23:45.678|app.shell.fetch_page('simple.wikipedia.org/wiki/Earth', 'html');";
|
||||
String command = String.format("app.shell.fetch_page('%s/wiki/%s', 'wiki');", wiki_domain, page);
|
||||
Send_message(command);
|
||||
}
|
||||
private void Send_message_server_stop() {
|
||||
Print_message_line("Sending request to stop server");
|
||||
Send_message("app.server.stop;");
|
||||
}
|
||||
private void Send_message(String command) {
|
||||
String id = "id_is_for_client_usage";
|
||||
String time = "time_is_for_client_usage";
|
||||
String body = String.format("xowa.cmd.exec|%s|xowa_tcp_console|xowa_server|%s|%s", id, time, command);
|
||||
int body_len = body.length();
|
||||
int cksum = (body_len * 2) + 1;
|
||||
String msg = String.format("0|%s|%s|%s", String.format("%010d", body_len), String.format("%010d", cksum), body);
|
||||
|
||||
Xowa_tcp_receiver receiver = new Xowa_tcp_receiver(server_recv_port, max_length);
|
||||
new Thread(receiver).start();
|
||||
sender.Send_command(msg);
|
||||
}
|
||||
private boolean Parse_args(String[] args) {
|
||||
if (args.length != 4) {
|
||||
Print_message_line("4 arguments must be supplied: " + args.length);
|
||||
return false;
|
||||
}
|
||||
server_send_port = Parse_int(args[0]); if (server_send_port == -1) return false;
|
||||
server_recv_port = Parse_int(args[1]); if (server_recv_port == -1) return false;
|
||||
wiki_domain = args[2];
|
||||
max_length = Parse_int(args[3]); if (max_length == -1) return false;
|
||||
return true;
|
||||
}
|
||||
private static int Parse_int(String raw) {
|
||||
try {return Integer.parseInt(raw);}
|
||||
catch (Exception e) {
|
||||
Print_message_line("argument must be numeric: " + raw);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
private static String Read_string(BufferedReader input) {
|
||||
try {return input.readLine();}
|
||||
catch (IOException e) {return null;}
|
||||
}
|
||||
public static void Print_message_line(String msg) {
|
||||
System.out.println(msg);
|
||||
}
|
||||
public static void Print_message(String msg) {
|
||||
System.out.print(msg);
|
||||
}
|
||||
public static void Print_error(Exception e) {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
public static void Sleep(long millis) {
|
||||
try {Thread.sleep(millis);}
|
||||
catch (InterruptedException e) {Print_error(e);}
|
||||
}
|
||||
}
|
||||
class Xowa_tcp_sender {
|
||||
private int port;
|
||||
private Socket socket;
|
||||
private OutputStream output_stream;
|
||||
public Xowa_tcp_sender(int port) {this.port = port;}
|
||||
public boolean Open_socket() {
|
||||
try {
|
||||
socket = new Socket("localhost", port);
|
||||
// socket.setSoTimeout(10000);
|
||||
output_stream = socket.getOutputStream();
|
||||
return true;
|
||||
}
|
||||
catch (Exception e) {
|
||||
Xowa_tcp_console.Print_error(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public void Send_command(String msg) {
|
||||
try {
|
||||
while (!Open_socket()) {
|
||||
Xowa_tcp_console.Sleep(100);
|
||||
}
|
||||
byte[] buffer = msg.getBytes();
|
||||
output_stream.write(buffer, 0, buffer.length);
|
||||
Close_socket();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Xowa_tcp_console.Print_error(e);
|
||||
}
|
||||
}
|
||||
public void Close_socket() {
|
||||
try {
|
||||
output_stream.close();
|
||||
socket.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Xowa_tcp_console.Print_error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
class Xowa_tcp_receiver implements Runnable {
|
||||
private int port;
|
||||
private int max_length;
|
||||
private ServerSocket server_socket;
|
||||
private Socket client_socket;
|
||||
private InputStream input_stream;
|
||||
public Xowa_tcp_receiver(int port, int max_length) {
|
||||
this.port = port;
|
||||
this.max_length = max_length;
|
||||
}
|
||||
public void run() {
|
||||
try {
|
||||
// initialization
|
||||
server_socket = new ServerSocket(port);
|
||||
client_socket = server_socket.accept();
|
||||
// client_socket.setSoTimeout(10000);
|
||||
byte[] buffer = new byte[65536];
|
||||
input_stream = client_socket.getInputStream();
|
||||
|
||||
// read incoming messages
|
||||
int read = 0;
|
||||
while (true) {
|
||||
String msg = "";
|
||||
// read header
|
||||
int body_len_max = 0, body_len_cur = 0;
|
||||
read = input_stream.read(buffer, 0, 24);
|
||||
if (read == -1) break;
|
||||
String body_len_max_str = new String(buffer, 2, 10);
|
||||
body_len_max = Integer.parseInt(body_len_max_str);
|
||||
buffer = new byte[body_len_max];
|
||||
|
||||
// read rest of body
|
||||
while (body_len_cur < body_len_max) {
|
||||
read = input_stream.read(buffer);
|
||||
if (read == -1) break;
|
||||
body_len_cur += read;
|
||||
msg += new String(buffer, 0, read);
|
||||
}
|
||||
|
||||
int msg_length = msg.length();
|
||||
if (msg_length > max_length) msg_length = max_length;
|
||||
Xowa_tcp_console.Print_message_line(msg.substring(0, msg_length));
|
||||
Xowa_tcp_console.Print_message("\n\n> ");
|
||||
}
|
||||
this.Close_socket();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Xowa_tcp_console.Print_error(e);
|
||||
}
|
||||
}
|
||||
public void Close_socket() {
|
||||
try {
|
||||
input_stream.close();
|
||||
client_socket.close();
|
||||
server_socket.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Xowa_tcp_console.Print_error(e); // ignore, else error will print in console
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user