1
0
mirror of https://github.com/gnosygnu/xowa.git synced 2026-03-02 03:49:30 +00:00

Source: Restore broken commit

This commit is contained in:
gnosygnu
2017-02-06 22:14:55 -05:00
parent 938beac9f9
commit 3bfeb94b43
4380 changed files with 328018 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@@ -0,0 +1,36 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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"));
}

View File

@@ -0,0 +1,27 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;
}

View File

@@ -0,0 +1,50 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@@ -0,0 +1,150 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
This file is part of XOWA: the XOWA Offline Wiki Application
Copyright (C) 2013 matthiasjasny@gmail.com
This file is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This file is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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) {port = v; return this;} private int port = 8080;
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, ttl_bry);
// 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"));
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";
}

View File

@@ -0,0 +1,64 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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";
}

View File

@@ -0,0 +1,182 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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{
client_wtr.Write_bry(Rsp__http_ok);
// 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(Rsp__content_type_html);
client_wtr.Write_bry(Byte_ascii.Nl_bry);
client_wtr.Write_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")
;
}

View File

@@ -0,0 +1,39 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@@ -0,0 +1,42 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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));
}
}

View File

@@ -0,0 +1,43 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;
}
}
}

View File

@@ -0,0 +1,55 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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);}
}
}

View File

@@ -0,0 +1,51 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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);}
}
}

View File

@@ -0,0 +1,25 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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")
;
}

View File

@@ -0,0 +1,121 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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');"*/

View File

@@ -0,0 +1,64 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@@ -0,0 +1,65 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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());
}
}

View File

@@ -0,0 +1,130 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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_adp_.Eq_typeSafe(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());
}
}
}

View File

@@ -0,0 +1,40 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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);
}
}

View File

@@ -0,0 +1,48 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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";
}

View File

@@ -0,0 +1,34 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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();
}
}

View File

@@ -0,0 +1,230 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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
}
}
}