Mercurial Hosting > luan
view src/luan/modules/IoLuan.java @ 1171:794ddcfbee20
remove http/impl
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Sun, 11 Feb 2018 02:41:23 -0700 |
parents | e54ae41e9501 |
children | 51d1342e25ad |
line wrap: on
line source
package luan.modules; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.Reader; import java.io.Writer; import java.io.StringReader; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.StringWriter; import java.io.IOException; import java.io.FileNotFoundException; import java.net.URL; import java.net.Socket; import java.net.ServerSocket; import java.net.InetAddress; import java.net.Inet4Address; import java.net.NetworkInterface; import java.net.MalformedURLException; import java.net.UnknownHostException; import java.util.Enumeration; import java.util.List; import java.util.Map; import luan.Luan; import luan.LuanState; import luan.LuanTable; import luan.LuanFunction; import luan.LuanJavaFunction; import luan.LuanException; import luan.modules.url.LuanUrl; public final class IoLuan { private static void add(LuanTable t,String method,Class... parameterTypes) throws NoSuchMethodException { t.rawPut( method, new LuanJavaFunction(IoLuan.class.getMethod(method,parameterTypes),null) ); } public static String read_console_line(String prompt) throws IOException { if( prompt==null ) prompt = "> "; return System.console().readLine(prompt); } public interface LuanWriter { public void write(LuanState luan,Object... args) throws LuanException, IOException; public void close() throws IOException; } public static LuanTable textWriter(final PrintStream out) { LuanWriter luanWriter = new LuanWriter() { public void write(LuanState luan,Object... args) throws LuanException { for( Object obj : args ) { out.print( luan.toString(obj) ); } } public void close() { out.close(); } }; return writer(luanWriter); } public static LuanTable textWriter(final Writer out) { LuanWriter luanWriter = new LuanWriter() { public void write(LuanState luan,Object... args) throws LuanException, IOException { for( Object obj : args ) { out.write( luan.toString(obj) ); } } public void close() throws IOException { out.close(); } }; return writer(luanWriter); } private static LuanTable writer(LuanWriter luanWriter) { LuanTable writer = new LuanTable(); try { writer.rawPut( "write", new LuanJavaFunction( LuanWriter.class.getMethod( "write", LuanState.class, new Object[0].getClass() ), luanWriter ) ); writer.rawPut( "close", new LuanJavaFunction( LuanWriter.class.getMethod( "close" ), luanWriter ) ); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } return writer; } public static LuanTable binaryWriter(final OutputStream out) { LuanTable writer = new LuanTable(); try { writer.rawPut( "write", new LuanJavaFunction( OutputStream.class.getMethod( "write", new byte[0].getClass() ), out ) ); writer.rawPut( "close", new LuanJavaFunction( OutputStream.class.getMethod( "close" ), out ) ); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } return writer; } static LuanFunction lines(final BufferedReader in) { return new LuanFunction() { @Override public Object call(LuanState luan,Object[] args) throws LuanException { try { if( args.length > 0 ) { if( args.length > 1 || !"close".equals(args[0]) ) throw new LuanException( "the only argument allowed is 'close'" ); in.close(); return null; } String rtn = in.readLine(); if( rtn==null ) in.close(); return rtn; } catch(IOException e) { throw new LuanException(e); } } }; } static LuanFunction blocks(final InputStream in,final int blockSize) { return new LuanFunction() { final byte[] a = new byte[blockSize]; @Override public Object call(LuanState luan,Object[] args) throws LuanException { try { if( args.length > 0 ) { if( args.length > 1 || !"close".equals(args[0]) ) throw new LuanException( "the only argument allowed is 'close'" ); in.close(); return null; } if( in.read(a) == -1 ) { in.close(); return null; } return a; } catch(IOException e) { throw new LuanException(e); } } }; } private static File objToFile(Object obj) { if( obj instanceof String ) { return new File((String)obj); } if( obj instanceof LuanTable ) { LuanTable t = (LuanTable)obj; Object java = t.rawGet("java"); if( java instanceof LuanFile ) { LuanFile luanFile = (LuanFile)java; return luanFile.file; } } return null; } public static abstract class LuanIn { public abstract InputStream inputStream() throws IOException, LuanException; public abstract String to_string(); public abstract String to_uri_string(); public Reader reader() throws IOException, LuanException { return new InputStreamReader(inputStream()); } public String read_text() throws IOException, LuanException { Reader in = reader(); String s = Utils.readAll(in); in.close(); return s; } public byte[] read_binary() throws IOException, LuanException { InputStream in = inputStream(); byte[] a = Utils.readAll(in); in.close(); return a; } public LuanFunction read_lines() throws IOException, LuanException { return lines(new BufferedReader(reader())); } public LuanFunction read_blocks(Integer blockSize) throws IOException, LuanException { int n = blockSize!=null ? blockSize : Utils.bufSize; return blocks(inputStream(),n); } public boolean exists() throws IOException, LuanException { try { inputStream().close(); return true; } catch(FileNotFoundException e) { return false; } } public long checksum() throws IOException, LuanException { long cs = 0; InputStream in = new BufferedInputStream(inputStream()); int c; while( (c=in.read()) != -1 ) { cs = 31 * cs + c; } in.close(); return cs; } public LuanTable table() { LuanTable tbl = new LuanTable(); try { tbl.rawPut( "java", this ); tbl.rawPut( "to_string", new LuanJavaFunction( LuanIn.class.getMethod( "to_string" ), this ) ); tbl.rawPut( "to_uri_string", new LuanJavaFunction( LuanIn.class.getMethod( "to_uri_string" ), this ) ); tbl.rawPut( "read_text", new LuanJavaFunction( LuanIn.class.getMethod( "read_text" ), this ) ); tbl.rawPut( "read_binary", new LuanJavaFunction( LuanIn.class.getMethod( "read_binary" ), this ) ); tbl.rawPut( "read_lines", new LuanJavaFunction( LuanIn.class.getMethod( "read_lines" ), this ) ); tbl.rawPut( "read_blocks", new LuanJavaFunction( LuanIn.class.getMethod( "read_blocks", Integer.class ), this ) ); tbl.rawPut( "exists", new LuanJavaFunction( LuanIn.class.getMethod( "exists" ), this ) ); tbl.rawPut( "checksum", new LuanJavaFunction( LuanIn.class.getMethod( "checksum" ), this ) ); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } return tbl; } } public static final LuanIn defaultStdin = new LuanIn() { @Override public InputStream inputStream() { return System.in; } @Override public String to_string() { return "<stdin>"; } @Override public String to_uri_string() { return "stdin:"; } @Override public String read_text() throws IOException { return Utils.readAll(new InputStreamReader(System.in)); } @Override public byte[] read_binary() throws IOException { return Utils.readAll(System.in); } @Override public boolean exists() { return true; } }; public static abstract class LuanIO extends LuanIn { abstract OutputStream outputStream() throws IOException; public void write(Object obj) throws LuanException, IOException { if( obj instanceof String ) { String s = (String)obj; Writer out = new OutputStreamWriter(outputStream()); out.write(s); out.close(); return; } if( obj instanceof byte[] ) { byte[] a = (byte[])obj; OutputStream out = outputStream(); Utils.copyAll(new ByteArrayInputStream(a),out); out.close(); return; } if( obj instanceof LuanTable ) { LuanTable t = (LuanTable)obj; Object java = t.rawGet("java"); if( java instanceof LuanIn ) { LuanIn luanIn = (LuanIn)java; InputStream in = luanIn.inputStream(); OutputStream out = outputStream(); Utils.copyAll(in,out); out.close(); in.close(); return; } } throw new LuanException( "bad argument #1 to 'write' (string or binary or Io.uri expected)" ); } public LuanTable text_writer() throws IOException { return textWriter(new BufferedWriter(new OutputStreamWriter(outputStream()))); } public LuanTable binary_writer() throws IOException { return binaryWriter(new BufferedOutputStream(outputStream())); } @Override public LuanTable table() { LuanTable tbl = super.table(); try { tbl.rawPut( "write", new LuanJavaFunction( LuanIO.class.getMethod( "write", Object.class ), this ) ); tbl.rawPut( "text_writer", new LuanJavaFunction( LuanIO.class.getMethod( "text_writer" ), this ) ); tbl.rawPut( "binary_writer", new LuanJavaFunction( LuanIO.class.getMethod( "binary_writer" ), this ) ); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } return tbl; } } private static final LuanIO nullIO = new LuanIO() { private final InputStream in = new InputStream() { @Override public int read() { return -1; } }; private final OutputStream out = new OutputStream() { @Override public void write(int b) {} }; @Override public InputStream inputStream() { return in; } @Override OutputStream outputStream() { return out; } @Override public String to_string() { return "<null>"; } @Override public String to_uri_string() { return "null:"; } }; public static final class LuanString extends LuanIO { private String s; private LuanString(String s) { this.s = s; } @Override public InputStream inputStream() { throw new UnsupportedOperationException(); } @Override OutputStream outputStream() { throw new UnsupportedOperationException(); } @Override public String to_string() { return "<string>"; } @Override public String to_uri_string() { return "string:" + s; } @Override public Reader reader() { return new StringReader(s); } @Override public String read_text() { return s; } @Override public boolean exists() { return true; } @Override public LuanTable text_writer() throws IOException { LuanWriter luanWriter = new LuanWriter() { private final Writer out = new StringWriter(); public void write(LuanState luan,Object... args) throws LuanException, IOException { for( Object obj : args ) { out.write( luan.toString(obj) ); } } public void close() throws IOException { s = out.toString(); } }; return writer(luanWriter); } } public static final class LuanFile extends LuanIO { public final File file; private LuanFile(LuanState luan,File file) throws LuanException { this(file); check(luan,"file:"+file.toString()); } private LuanFile(File file) { this.file = file; } @Override public InputStream inputStream() throws IOException { return new FileInputStream(file); } @Override OutputStream outputStream() throws IOException { return new FileOutputStream(file); } @Override public String to_string() { return file.toString(); } @Override public String to_uri_string() { return "file:" + file.toString(); } public LuanTable child(LuanState luan,String name) throws LuanException { return new LuanFile(luan,new File(file,name)).table(); } public LuanTable children(LuanState luan) throws LuanException { File[] files = file.listFiles(); if( files==null ) return null; LuanTable list = new LuanTable(); for( File f : files ) { list.rawPut(list.rawLength()+1,new LuanFile(luan,f).table()); } return list; } public LuanTable parent(LuanState luan) throws LuanException, IOException { File parent = file.getParentFile(); if( parent==null ) parent = file.getCanonicalFile().getParentFile(); return new LuanFile(luan,parent).table(); } @Override public boolean exists() { return file.exists(); } public void rename_to(Object destObj) throws LuanException { File dest = objToFile(destObj); if( dest==null ) throw new LuanException( "bad argument #1 to 'objToFile' (string or file table expected)" ); if( !file.renameTo(dest) ) throw new LuanException("couldn't rename file "+file+" to "+dest); } public LuanTable canonical(LuanState luan) throws LuanException, IOException { return new LuanFile(luan,file.getCanonicalFile()).table(); } public LuanTable create_temp_file(LuanState luan,String prefix,String suffix) throws LuanException, IOException { File tmp = File.createTempFile(prefix,suffix,file); return new LuanFile(luan,tmp).table(); } public void delete() throws LuanException { if( file.exists() ) delete(file); } private static void delete(File file) throws LuanException { File[] children = file.listFiles(); if( children != null ) { for( File child : children ) { delete(child); } } if( !file.delete() ) throw new LuanException("couldn't delete file "+file); } public void mkdir() throws LuanException { if( !file.isDirectory() ) { if( !file.mkdirs() ) throw new LuanException("couldn't make directory "+file); } } public void set_last_modified(long time) throws LuanException { if( !file.setLastModified(time) ) throw new LuanException("couldn't set_last_modified on "+file); } @Override public LuanTable table() { LuanTable tbl = super.table(); try { tbl.rawPut( "name", new LuanJavaFunction( File.class.getMethod( "getName" ), file ) ); tbl.rawPut( "is_directory", new LuanJavaFunction( File.class.getMethod( "isDirectory" ), file ) ); tbl.rawPut( "is_file", new LuanJavaFunction( File.class.getMethod( "isFile" ), file ) ); tbl.rawPut( "delete", new LuanJavaFunction( LuanFile.class.getMethod( "delete" ), this ) ); tbl.rawPut( "delete_on_exit", new LuanJavaFunction( File.class.getMethod( "deleteOnExit" ), file ) ); tbl.rawPut( "mkdir", new LuanJavaFunction( LuanFile.class.getMethod( "mkdir" ), this ) ); tbl.rawPut( "last_modified", new LuanJavaFunction( File.class.getMethod( "lastModified" ), file ) ); tbl.rawPut( "set_last_modified", new LuanJavaFunction( LuanFile.class.getMethod( "set_last_modified", Long.TYPE ), this ) ); tbl.rawPut( "length", new LuanJavaFunction( File.class.getMethod( "length" ), file ) ); tbl.rawPut( "child", new LuanJavaFunction( LuanFile.class.getMethod( "child", LuanState.class, String.class ), this ) ); tbl.rawPut( "children", new LuanJavaFunction( LuanFile.class.getMethod( "children", LuanState.class ), this ) ); tbl.rawPut( "parent", new LuanJavaFunction( LuanFile.class.getMethod( "parent", LuanState.class ), this ) ); tbl.rawPut( "rename_to", new LuanJavaFunction( LuanFile.class.getMethod( "rename_to", Object.class ), this ) ); tbl.rawPut( "canonical", new LuanJavaFunction( LuanFile.class.getMethod( "canonical", LuanState.class ), this ) ); tbl.rawPut( "create_temp_file", new LuanJavaFunction( LuanFile.class.getMethod( "create_temp_file", LuanState.class, String.class, String.class ), this ) ); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } return tbl; } } public static LuanTable null_(String ignore) { return nullIO.table(); } public static LuanTable string(String s) throws LuanException { Utils.checkNotNull(s); return new LuanString(s).table(); } public static LuanTable file(LuanState luan,String name) throws LuanException { File file = new File(name); return new LuanFile(luan,file).table(); } public static LuanTable classpath(LuanState luan,String name) throws LuanException { if( name.contains("//") ) return null; String path = name; check(luan,"classpath:"+path); URL url; if( !path.contains("#") ) { url = ClassLoader.getSystemResource(path); } else { String[] a = path.split("#"); url = ClassLoader.getSystemResource(a[0]); if( url==null ) { for( int i=1; i<a.length; i++ ) { url = ClassLoader.getSystemResource(a[0]+"/"+a[i]); if( url != null ) { try { url = new URL(url,"."); } catch(MalformedURLException e) { throw new RuntimeException(e); } break; } } } } if( url != null ) return new LuanUrl(luan,url,null).table(); return null; } private static LuanTable url(LuanState luan,String url,LuanTable options) throws IOException, LuanException { return new LuanUrl(luan,new URL(url),options).table(); } public static LuanTable http(LuanState luan,String path,LuanTable options) throws IOException, LuanException { return url(luan,"http:"+path,options); } public static LuanTable https(LuanState luan,String path,LuanTable options) throws IOException, LuanException { return url(luan,"https:"+path,options); } public static LuanTable luan(LuanState luan,String path) throws LuanException { return classpath( luan, "luan/modules/" + path ); } public static LuanTable stdin(LuanState luan) throws LuanException { LuanTable io = (LuanTable)PackageLuan.require(luan,"luan:Io.luan"); return (LuanTable)io.get(luan,"stdin"); } public static LuanTable newSchemes() { LuanTable schemes = new LuanTable(); try { schemes.rawPut( "null", new LuanJavaFunction(IoLuan.class.getMethod("null_",String.class),null) ); add( schemes, "string", String.class ); add( schemes, "file", LuanState.class, String.class ); add( schemes, "classpath", LuanState.class, String.class ); add( schemes, "socket", String.class ); add( schemes, "http", LuanState.class, String.class, LuanTable.class ); add( schemes, "https", LuanState.class, String.class, LuanTable.class ); add( schemes, "luan", LuanState.class, String.class ); add( schemes, "stdin", LuanState.class ); add( schemes, "os", LuanState.class, String.class, LuanTable.class ); add( schemes, "bash", LuanState.class, String.class, LuanTable.class ); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } return schemes; } private static LuanTable schemes(LuanState luan) throws LuanException { LuanTable t = (LuanTable)PackageLuan.loaded(luan).rawGet("luan:Io.luan"); if( t == null ) return newSchemes(); t = (LuanTable)t.get(luan,"schemes"); if( t == null ) return newSchemes(); return t; } public static LuanTable uri(LuanState luan,String name,LuanTable options) throws LuanException { int i = name.indexOf(':'); if( i == -1 ) throw new LuanException( "invalid Io.uri name '"+name+"', missing scheme" ); String scheme = name.substring(0,i); String location = name.substring(i+1); LuanTable schemes = schemes(luan); LuanFunction opener = (LuanFunction)schemes.get(luan,scheme); if( opener == null ) throw new LuanException( "invalid scheme '"+scheme+"' in '"+name+"'" ); return (LuanTable)Luan.first(opener.call(luan,new Object[]{location,options})); } public static final class LuanSocket extends LuanIO { public final Socket socket; private LuanSocket(String host,int port) throws LuanException { try { this.socket = new Socket(host,port); } catch(IOException e) { throw new LuanException(e.toString()); } } private LuanSocket(Socket socket) { this.socket = socket; } @Override public InputStream inputStream() throws IOException { return socket.getInputStream(); } @Override OutputStream outputStream() throws IOException { return socket.getOutputStream(); } @Override public String to_string() { return socket.toString(); } @Override public String to_uri_string() { throw new UnsupportedOperationException(); } } public static LuanTable socket(String name) throws LuanException, IOException { int i = name.indexOf(':'); if( i == -1 ) throw new LuanException( "invalid socket '"+name+"', format is: <host>:<port>" ); String host = name.substring(0,i); String portStr = name.substring(i+1); int port = Integer.parseInt(portStr); return new LuanSocket(host,port).table(); } public static LuanFunction socket_server(int port) throws IOException { final ServerSocket ss = new ServerSocket(port); return new LuanFunction() { @Override public Object call(LuanState luan,Object[] args) throws LuanException { try { if( args.length > 0 ) { if( args.length > 1 || !"close".equals(args[0]) ) throw new LuanException( "the only argument allowed is 'close'" ); ss.close(); return null; } return new LuanSocket(ss.accept()).table(); } catch(IOException e) { throw new LuanException(e); } } }; } public static class BaseOs extends LuanIO { private final String cmd; final File dir; Process proc; private BaseOs(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException { this.cmd = cmd; File dir = null; if( options != null ) { Map map = options.asMap(luan); Object obj = map.remove("dir"); dir = objToFile(obj); if( dir==null ) throw new LuanException( "bad option 'dir' (string or file table expected)" ); if( !map.isEmpty() ) throw new LuanException( "unrecognized options: "+map ); } this.dir = dir; } @Override public InputStream inputStream() throws IOException { return proc.getInputStream(); } @Override OutputStream outputStream() throws IOException { return proc.getOutputStream(); } @Override public String to_string() { return proc.toString(); } @Override public String to_uri_string() { throw new UnsupportedOperationException(); } @Override public boolean exists() { return true; } public void wait_for() throws IOException, LuanException { try { proc.waitFor(); } catch(InterruptedException e) { throw new RuntimeException(e); } int exitVal = proc.exitValue(); if( exitVal != 0 ) { Reader err = new InputStreamReader(proc.getErrorStream()); String error = "exit value "+exitVal+" in: "+cmd+"\n"+Utils.readAll(err); err.close(); throw new LuanException(error); } } @Override public String read_text() throws IOException, LuanException { String s = super.read_text(); wait_for(); return s; } @Override public LuanTable table() { LuanTable tbl = super.table(); try { tbl.rawPut( "wait_for", new LuanJavaFunction( BaseOs.class.getMethod( "wait_for" ), this ) ); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } return tbl; } } public static final class LuanOs extends BaseOs { private LuanOs(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException { super(luan,cmd,options); check(luan,"os:"+cmd); this.proc = Runtime.getRuntime().exec(cmd,null,dir); } } public static LuanTable os(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException { return new LuanOs(luan,cmd,options).table(); } public static final class LuanBash extends BaseOs { private LuanBash(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException { super(luan,cmd,options); check(luan,"bash:"+cmd); this.proc = Runtime.getRuntime().exec(new String[]{"bash","-c",cmd},null,dir); } } public static LuanTable bash(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException { return new LuanBash(luan,cmd,options).table(); } public static class LuanInput extends LuanIn { private final InputStream in; public LuanInput(InputStream in) { this.in = in; } @Override public InputStream inputStream() { return in; } @Override public String to_string() { return "<input_stream>"; } @Override public String to_uri_string() { throw new UnsupportedOperationException(); } @Override public boolean exists() { return true; } }; public static String ip(String domain) { try { return InetAddress.getByName(domain).getHostAddress(); } catch(UnknownHostException e) { return null; } } public static LuanTable my_ips() throws IOException { LuanTable tbl = new LuanTable(); for( Enumeration<NetworkInterface> e1 = NetworkInterface.getNetworkInterfaces(); e1.hasMoreElements(); ) { NetworkInterface ni = e1.nextElement(); for( Enumeration<InetAddress> e2 = ni.getInetAddresses(); e2.hasMoreElements(); ) { InetAddress ia = e2.nextElement(); if( ia instanceof Inet4Address ) tbl.rawPut(ia.getHostAddress(),true); } } return tbl; } /* // files maps zip name to uri public static void zip(LuanState luan,String zipUri,LuanTable files) throws LuanException, IOException { Object obj = uri(luan,zipUri,null).rawGet("java"); if( !(obj instanceof LuanIO) ) throw new LuanException("invalid uri for zip"); LuanIO zipIo = (LuanIO)obj; ZipOutputStream out = new ZipOutputStream(zipIo.outputStream()); for( Map.Entry<Object,Object> entry : files.iterable(luan) ) { obj = entry.getKey(); if( !(obj instanceof String) ) throw new LuanException("zip file table keys must be strings"); String fileName = (String)obj; obj = entry.getValue(); if( !(obj instanceof String) ) throw new LuanException("zip file table values must be strings"); String uriStr = (String)obj; out.putNextEntry(new ZipEntry(fileName)); obj = uri(luan,uriStr,null).rawGet("java"); if( !(obj instanceof LuanIn) ) throw new LuanException("invalid uri for zip"); LuanIn zipIn = (LuanIn)obj; InputStream in = zipIn.inputStream(); Utils.copyAll(in,out); in.close(); out.closeEntry(); } out.close(); } */ // security public static void unrestricted(LuanState luan) throws LuanException { JavaLuan.check(luan); luan.javaOk.unrestrictedIo = true; } public interface Security { public void check(LuanState luan,String name) throws LuanException; } private static String SECURITY_KEY = "Io.Security"; private static void check(LuanState luan,String name) throws LuanException { if( luan.javaOk.unrestrictedIo ) return; Security s = (Security)luan.registry().get(SECURITY_KEY); if( s!=null ) s.check(luan,name); } public static void setSecurity(LuanState luan,Security s) { luan.registry().put(SECURITY_KEY,s); } private void IoLuan() {} // never }