Mercurial Hosting > luan
changeset 610:b4f3dbe1c6e3
add Rpc and change Hosting to use Rpc
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Fri, 11 Dec 2015 00:13:13 -0700 |
parents | 24b05963ba62 |
children | 8bd98da6991a |
files | core/src/luan/modules/IoLuan.java core/src/luan/modules/Rpc.luan core/src/luan/modules/RpcLuan.java core/src/luan/modules/Time.luan core/src/luan/modules/host/Hosting.luan |
diffstat | 5 files changed, 316 insertions(+), 19 deletions(-) [+] |
line wrap: on
line diff
--- a/core/src/luan/modules/IoLuan.java Wed Dec 09 18:12:16 2015 -0700 +++ b/core/src/luan/modules/IoLuan.java Fri Dec 11 00:13:13 2015 -0700 @@ -206,6 +206,7 @@ public LuanTable table() { LuanTable tbl = new LuanTable(); try { + tbl.rawPut( "java", this ); tbl.rawPut( "to_string", new LuanJavaFunction( LuanIn.class.getMethod( "to_string" ), this ) );
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/src/luan/modules/Rpc.luan Fri Dec 11 00:13:13 2015 -0700 @@ -0,0 +1,38 @@ +java() +local RpcLuan = require "java:luan.modules.RpcLuan" +local Luan = require "luan:Luan" +local error = Luan.error +local set_metatable = Luan.set_metatable or error() +local Io = require "luan:Io" +local Thread = require "luan:Thread" + + +local M = {} + +M.call = RpcLuan.call -- Rpc.call(socket,fn_name,...) +M.respond = RpcLuan.respond -- Rpc.respond(socket,fns) + +function M.remote(socket_uri) + local mt = {} + function mt.__index(_,key) + return function(...) + local socket = Io.uri(socket_uri) + return M.call(socket,key,...) + end + end + local t = {} + set_metatable(t,mt) + return t +end + +--[[ +function M.serve(port,fns) + local server = Io.socket_server(port) + while true do + local socket = server() + Thread.fork(function() M.respond(socket,fns) end) + end +end +]] + +return M
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/src/luan/modules/RpcLuan.java Fri Dec 11 00:13:13 2015 -0700 @@ -0,0 +1,228 @@ +package luan.modules; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.EOFException; +import java.nio.charset.StandardCharsets; +import java.util.Set; +import java.util.IdentityHashMap; +import java.util.Collections; +import java.util.Map; +import luan.Luan; +import luan.LuanState; +import luan.LuanTable; +import luan.LuanFunction; +import luan.LuanException; +import luan.LuanMethod; + + +public final class RpcLuan { + private static final int NIL = 0; + private static final int STRING = 1; + private static final int BOOLEAN = 2; + private static final int NUMBER = 3; + private static final int BINARY = 4; + private static final int TABLE = 5; + + @LuanMethod public static Object[] call(LuanState luan,LuanTable socketTbl,String fnName,Object... args) + throws LuanException, IOException + { + IoLuan.LuanSocket socket = (IoLuan.LuanSocket)socketTbl.rawGet("java"); + InputStream in = new BufferedInputStream(socket.inputStream()); + OutputStream out = new BufferedOutputStream(socket.outputStream()); + try { + writeString(out,fnName); + writeInt(out,args.length); + for( Object arg : args ) { + writeObj(out,luan,arg); + } + out.flush(); + boolean ok = readBoolean(in); + if( ok ) { + int n = readInt(in); + Object[] rtn = new Object[n]; + for( int i=0; i<n; i++ ) { + rtn[i] = readObj(in,luan); + } + return rtn; + } else { + String msg = readString(in); + throw new LuanException(luan,msg); + } + } finally { + out.close(); + in.close(); + } + } + + public static void respond(LuanState luan,LuanTable socketTbl,LuanTable fns) + throws IOException, LuanException + { + IoLuan.LuanSocket socket = (IoLuan.LuanSocket)socketTbl.rawGet("java"); + InputStream in = new BufferedInputStream(socket.inputStream()); + OutputStream out = new BufferedOutputStream(socket.outputStream()); + try { + Object[] rtn; + try { + String fnName = readString(in); + int nArgs = readInt(in); + Object[] args = new Object[nArgs]; + for( int i=0; i<nArgs; i++ ) { + args[i] = readObj(in,luan); + } + LuanFunction fn = (LuanFunction)fns.rawGet(fnName); + if( fn == null ) + throw new LuanException(luan, "function not found: " + fnName ); + rtn = Luan.array(fn.call(luan,args)); + } catch(LuanException e) { + writeBoolean(out,false); + writeString(out,e.getFullMessage()); + return; + } + writeBoolean(out,true); + writeInt(out,rtn.length); + for( Object obj : rtn ) { + writeObj(out,luan,obj); + } + } finally { + out.close(); + in.close(); + } + } + + static void writeObj(OutputStream out,LuanState luan,Object obj) throws IOException, LuanException { + if( obj == null ) { + out.write(NIL); + } + else if( obj instanceof String ) { + out.write(STRING); + writeString(out,(String)obj); + } + else if( obj instanceof Boolean ) { + out.write(BOOLEAN); + writeBoolean(out,(Boolean)obj); + } + else if( obj instanceof Number ) { + out.write(NUMBER); + writeString(out,obj.toString()); + } + else if( obj instanceof byte[] ) { + byte[] a = (byte[])obj; + out.write(BINARY); + writeInt(out,a.length); + out.write(a); + } + else if( obj instanceof LuanTable ) { + out.write(TABLE); + String s = pickle( luan, obj, Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) ); + writeString(out,s); + } + else + throw new LuanException(luan, "invalid type: " + obj.getClass() ); + } + + static Object readObj(InputStream in,LuanState luan) throws IOException, LuanException { + int type = in.read(); + switch(type) { + case NIL: + return null; + case STRING: + return readString(in); + case BOOLEAN: + return readBoolean(in); + case NUMBER: + return Double.valueOf(readString(in)); + case BINARY: + return readBinary(in,readInt(in)); + case TABLE: + String s = readString(in); + LuanFunction fn = BasicLuan.load(luan,s,"rpc-reader",null,true); + return fn.call(luan); + default: + throw new LuanException(luan, "invalid type: " + type ); + } + } + + static Boolean readBoolean(InputStream in) throws IOException { + return Boolean.valueOf(readString(in)); + } + + static String readString(InputStream in) throws IOException { + int len = readInt(in); + byte[] a = readBinary(in,len); + return new String(a,StandardCharsets.UTF_8); + } + + static int readInt(InputStream in) throws IOException { + int ch1 = in.read(); + int ch2 = in.read(); + int ch3 = in.read(); + int ch4 = in.read(); + if ((ch1 | ch2 | ch3 | ch4) < 0) + throw new EOFException(); + return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); + } + + static byte[] readBinary(InputStream in,int size) throws IOException { + byte[] a = new byte[size]; + int i = 0; + while( i < size ) { + int n = in.read(a,i,size-i); + if( n == -1 ) + throw new EOFException(); + i += n; + } + return a; + } + + static void writeBoolean(OutputStream out,Boolean b) throws IOException { + writeString(out,b.toString()); + } + + static void writeString(OutputStream out,String s) throws IOException { + byte[] a = s.getBytes(StandardCharsets.UTF_8); + writeInt(out,a.length); + out.write(a); + } + + static void writeInt(OutputStream out,int v) throws IOException { + out.write((v >>> 24) & 0xFF); + out.write((v >>> 16) & 0xFF); + out.write((v >>> 8) & 0xFF); + out.write((v >>> 0) & 0xFF); + } + + + private static String pickle(LuanState luan,Object obj,Set<LuanTable> set) throws LuanException { + if( obj == null ) + return "nil"; + if( obj instanceof Boolean ) + return obj.toString(); + if( obj instanceof Number ) + return Luan.toString((Number)obj); + if( obj instanceof String ) + return "\"" + Luan.stringEncode((String)obj) + "\""; + if( obj instanceof LuanTable ) { + LuanTable tbl = (LuanTable)obj; + if( !set.add(tbl) ) { + throw new LuanException(luan, "circular reference in table" ); + } + StringBuilder sb = new StringBuilder(); + sb.append( "{" ); + for( Map.Entry<Object,Object> entry : tbl.iterable(luan) ) { + sb.append( "[" ); + sb.append( pickle(luan,entry.getKey(),set) ); + sb.append( "]=" ); + sb.append( pickle(luan,entry.getValue(),set) ); + sb.append( ", " ); + } + sb.append( "}" ); + return sb.toString(); + } + throw new LuanException(luan, "invalid type: " + obj.getClass() ); + } + +}
--- a/core/src/luan/modules/Time.luan Wed Dec 09 18:12:16 2015 -0700 +++ b/core/src/luan/modules/Time.luan Fri Dec 11 00:13:13 2015 -0700 @@ -2,8 +2,8 @@ java() local Luan = require "luan:Luan" -local ipairs = Luan.ipairs local error = Luan.error +local ipairs = Luan.ipairs or error() local Table = require "luan:Table" local System = require "java:java.lang.System" local Calendar = require "java:java.util.Calendar"
--- a/core/src/luan/modules/host/Hosting.luan Wed Dec 09 18:12:16 2015 -0700 +++ b/core/src/luan/modules/host/Hosting.luan Fri Dec 11 00:13:13 2015 -0700 @@ -1,36 +1,66 @@ -- Hosting -local Io = require "luan:Io" local Luan = require "luan:Luan" local error = Luan.error +local ipairs = Luan.ipairs or error() +local pairs = Luan.pairs or error() +local Io = require "luan:Io" +local print = Io.print or error() +local Rpc = require "luan:Rpc" + local M = {} M.port = 9101 function M.push(domain,password,dir) - local f = Io.uri("file:"..dir) - f.exists() or error("directory '"..dir.."' not found") - f.is_directory() or error("'"..dir.."' is not a directory") + local my_dir = Io.uri("file:"..dir) + my_dir.exists() or error("directory '"..dir.."' not found") + my_dir.is_directory() or error("'"..dir.."' is not a directory") local socket = "socket:" .. domain .. ":" .. M.port - local pc = Io.uri(socket).pickle_client() - local pickle = pc.pickle - pc.call(%> - local Hosting = require "luan:host/Hosting" - Hosting.do_push(<%=pickle(domain)%>,<%=pickle(password)%>,<%=pickle(dir)%>) - <%) - pc.close() + local host = Rpc.remote(socket) + local tree = host.get(domain,password) + if tree == nil then + print("creating "..domain) + tree = host.create(domain,password) + end + + local function process(there_parent,there,here) + if here.is_file() then + if there == nil or there.last_modified < here.last_modified() then + print("copying "..here.to_string()) + host.copy_file(domain,password,there_parent.path,here.name(),here.read_binary()) + end + elseif here.is_directory() then + if here.name() == "local" then + return + end + if there == nil then + there = host.mkdir(domain,password,there_parent.path,here.name()) + end + for _, here_child in ipairs(here.children()) do + local name = here_child.name() + process(there,there.children[name],here_child) + there.children[name] = nil + end + for _, there_child in pairs(there.children) do + print("deleting "..there_child.name) + host.delete_unused(domain,password,there_child.path) + end + else + error "not file or dir" + end + end + + process( nil, tree, my_dir ) + + host.remove_handler(domain,password) end function M.delete(domain,password) local socket = "socket:" .. domain .. ":" .. M.port - local pc = Io.uri(socket).pickle_client() - local pickle = pc.pickle - pc.call(%> - local Hosting = require "luan:host/Hosting" - Hosting.do_delete(<%=pickle(domain)%>,<%=pickle(password)%>) - <%) - pc.close() + local host = Rpc.remote(socket) + host.delete(domain,password) end return M