mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 13:42:59 +00:00 
			
		
		
		
	Merge pull request #238 from SquidDev-CC/feature/binary-handles
Refactor the filesystem and HTTP code
This commit is contained in:
		| @@ -7,15 +7,17 @@ | |||||||
| package dan200.computercraft.core.apis; | package dan200.computercraft.core.apis; | ||||||
|  |  | ||||||
| import dan200.computercraft.api.lua.ILuaContext; | import dan200.computercraft.api.lua.ILuaContext; | ||||||
| import dan200.computercraft.api.lua.ILuaObject; |  | ||||||
| import dan200.computercraft.api.lua.LuaException; | import dan200.computercraft.api.lua.LuaException; | ||||||
|  | import dan200.computercraft.core.apis.handles.BinaryInputHandle; | ||||||
|  | import dan200.computercraft.core.apis.handles.BinaryOutputHandle; | ||||||
|  | import dan200.computercraft.core.apis.handles.EncodedInputHandle; | ||||||
|  | import dan200.computercraft.core.apis.handles.EncodedOutputHandle; | ||||||
| import dan200.computercraft.core.filesystem.FileSystem; | import dan200.computercraft.core.filesystem.FileSystem; | ||||||
| import dan200.computercraft.core.filesystem.FileSystemException; | import dan200.computercraft.core.filesystem.FileSystemException; | ||||||
| import dan200.computercraft.core.filesystem.IMountedFileBinary; |  | ||||||
| import dan200.computercraft.core.filesystem.IMountedFileNormal; |  | ||||||
|  |  | ||||||
| import javax.annotation.Nonnull; | import javax.annotation.Nonnull; | ||||||
| import java.io.IOException; | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  |  | ||||||
| @@ -259,33 +261,33 @@ public class FSAPI implements ILuaAPI | |||||||
|                 try { |                 try { | ||||||
|                     if( mode.equals( "r" ) ) { |                     if( mode.equals( "r" ) ) { | ||||||
|                         // Open the file for reading, then create a wrapper around the reader |                         // Open the file for reading, then create a wrapper around the reader | ||||||
|                         IMountedFileNormal reader = m_fileSystem.openForRead( path ); |                         InputStream reader = m_fileSystem.openForRead( path ); | ||||||
|                         return wrapBufferedReader( reader ); |                         return new Object[] { new EncodedInputHandle( reader ) }; | ||||||
|                          |                          | ||||||
|                     } else if( mode.equals( "w" ) ) { |                     } else if( mode.equals( "w" ) ) { | ||||||
|                         // Open the file for writing, then create a wrapper around the writer |                         // Open the file for writing, then create a wrapper around the writer | ||||||
|                         IMountedFileNormal writer = m_fileSystem.openForWrite( path, false ); |                         OutputStream writer = m_fileSystem.openForWrite( path, false ); | ||||||
|                         return wrapBufferedWriter( writer ); |                         return new Object[] { new EncodedOutputHandle( writer ) }; | ||||||
|                      |                      | ||||||
|                     } else if( mode.equals( "a" ) ) { |                     } else if( mode.equals( "a" ) ) { | ||||||
|                         // Open the file for appending, then create a wrapper around the writer |                         // Open the file for appending, then create a wrapper around the writer | ||||||
|                         IMountedFileNormal writer = m_fileSystem.openForWrite( path, true ); |                         OutputStream writer = m_fileSystem.openForWrite( path, true ); | ||||||
|                         return wrapBufferedWriter( writer ); |                         return new Object[] { new EncodedOutputHandle( writer ) }; | ||||||
|                                              |                                              | ||||||
|                     } else if( mode.equals( "rb" ) ) { |                     } else if( mode.equals( "rb" ) ) { | ||||||
|                         // Open the file for binary reading, then create a wrapper around the reader |                         // Open the file for binary reading, then create a wrapper around the reader | ||||||
|                         IMountedFileBinary reader = m_fileSystem.openForBinaryRead( path ); |                         InputStream reader = m_fileSystem.openForRead( path ); | ||||||
|                         return wrapInputStream( reader ); |                         return new Object[] { new BinaryInputHandle( reader ) }; | ||||||
|                          |                          | ||||||
|                     } else if( mode.equals( "wb" ) ) { |                     } else if( mode.equals( "wb" ) ) { | ||||||
|                         // Open the file for binary writing, then create a wrapper around the writer |                         // Open the file for binary writing, then create a wrapper around the writer | ||||||
|                         IMountedFileBinary writer = m_fileSystem.openForBinaryWrite( path, false ); |                         OutputStream writer = m_fileSystem.openForWrite( path, false ); | ||||||
|                         return wrapOutputStream( writer ); |                         return new Object[] { new BinaryOutputHandle( writer ) }; | ||||||
|                      |                      | ||||||
|                     } else if( mode.equals( "ab" ) ) { |                     } else if( mode.equals( "ab" ) ) { | ||||||
|                         // Open the file for binary appending, then create a wrapper around the reader |                         // Open the file for binary appending, then create a wrapper around the reader | ||||||
|                         IMountedFileBinary writer = m_fileSystem.openForBinaryWrite( path, true ); |                         OutputStream writer = m_fileSystem.openForWrite( path, true ); | ||||||
|                         return wrapOutputStream( writer ); |                         return new Object[] { new BinaryOutputHandle( writer ) }; | ||||||
|                          |                          | ||||||
|                     } else { |                     } else { | ||||||
|                         throw new LuaException( "Unsupported mode" ); |                         throw new LuaException( "Unsupported mode" ); | ||||||
| @@ -368,288 +370,4 @@ public class FSAPI implements ILuaAPI | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |  | ||||||
|     private static Object[] wrapBufferedReader( final IMountedFileNormal reader ) |  | ||||||
|     { |  | ||||||
|         return new Object[] { new ILuaObject() { |  | ||||||
|             private boolean open = true; |  | ||||||
|  |  | ||||||
|             @Nonnull |  | ||||||
|             @Override |  | ||||||
|             public String[] getMethodNames() |  | ||||||
|             { |  | ||||||
|                 return new String[] { |  | ||||||
|                     "readLine", |  | ||||||
|                     "readAll", |  | ||||||
|                     "close" |  | ||||||
|                 }; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             @Override |  | ||||||
|             public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException |  | ||||||
|             { |  | ||||||
|                 switch( method ) |  | ||||||
|                 { |  | ||||||
|                     case 0: |  | ||||||
|                     { |  | ||||||
|                         // readLine |  | ||||||
|                         if( !open ) throw new LuaException( "attempt to use a closed file" ); |  | ||||||
|                         try { |  | ||||||
|                             String line = reader.readLine(); |  | ||||||
|                             if( line != null ) { |  | ||||||
|                                 return new Object[] { line }; |  | ||||||
|                             } else { |  | ||||||
|                                 return null; |  | ||||||
|                             } |  | ||||||
|                         } catch( IOException e ) { |  | ||||||
|                             return null; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     case 1: |  | ||||||
|                     { |  | ||||||
|                         // readAll |  | ||||||
|                         if( !open ) throw new LuaException( "attempt to use a closed file" ); |  | ||||||
|                         try { |  | ||||||
|                             StringBuilder result = new StringBuilder( "" ); |  | ||||||
|                             String line = reader.readLine(); |  | ||||||
|                             while( line != null ) { |  | ||||||
|                                 result.append( line ); |  | ||||||
|                                 line = reader.readLine(); |  | ||||||
|                                 if( line != null ) { |  | ||||||
|                                     result.append( "\n" ); |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                             return new Object[] { result.toString() }; |  | ||||||
|                         } catch( IOException e ) { |  | ||||||
|                             return null; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     case 2: |  | ||||||
|                     { |  | ||||||
|                         // close |  | ||||||
|                         try { |  | ||||||
|                             reader.close(); |  | ||||||
|                             open = false; |  | ||||||
|                             return null; |  | ||||||
|                         } catch( IOException e ) { |  | ||||||
|                             return null; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     default: |  | ||||||
|                     { |  | ||||||
|                         return null; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } }; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private static Object[] wrapBufferedWriter( final IMountedFileNormal writer ) |  | ||||||
|     { |  | ||||||
|         return new Object[] { new ILuaObject() { |  | ||||||
|             private boolean open = true; |  | ||||||
|  |  | ||||||
|             @Nonnull |  | ||||||
|             @Override |  | ||||||
|             public String[] getMethodNames() |  | ||||||
|             { |  | ||||||
|                 return new String[] { |  | ||||||
|                     "write", |  | ||||||
|                     "writeLine", |  | ||||||
|                     "close", |  | ||||||
|                     "flush" |  | ||||||
|                 }; |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             @Override |  | ||||||
|             public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException |  | ||||||
|             { |  | ||||||
|                 switch( method ) |  | ||||||
|                 { |  | ||||||
|                     case 0: |  | ||||||
|                     { |  | ||||||
|                         // write |  | ||||||
|                         if( !open ) throw new LuaException( "attempt to use a closed file" ); |  | ||||||
|                         String text; |  | ||||||
|                         if( args.length > 0 && args[0] != null ) { |  | ||||||
|                             text = args[0].toString(); |  | ||||||
|                         } else { |  | ||||||
|                             text = ""; |  | ||||||
|                         } |  | ||||||
|                         try { |  | ||||||
|                             writer.write( text, 0, text.length(), false ); |  | ||||||
|                             return null; |  | ||||||
|                         } catch( IOException e ) { |  | ||||||
|                             throw new LuaException( e.getMessage() ); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     case 1: |  | ||||||
|                     { |  | ||||||
|                         // writeLine |  | ||||||
|                         if( !open ) throw new LuaException( "attempt to use a closed file" ); |  | ||||||
|                          |  | ||||||
|                         String text; |  | ||||||
|                         if( args.length > 0 && args[0] != null ) { |  | ||||||
|                             text = args[0].toString(); |  | ||||||
|                         } else { |  | ||||||
|                             text = ""; |  | ||||||
|                         } |  | ||||||
|                         try { |  | ||||||
|                             writer.write( text, 0, text.length(), true ); |  | ||||||
|                             return null; |  | ||||||
|                         } catch( IOException e ) { |  | ||||||
|                             throw new LuaException( e.getMessage() ); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     case 2: |  | ||||||
|                     { |  | ||||||
|                         // close |  | ||||||
|                         try { |  | ||||||
|                             writer.close(); |  | ||||||
|                             open = false; |  | ||||||
|                             return null; |  | ||||||
|                         } catch( IOException e ) { |  | ||||||
|                             return null; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     case 3: |  | ||||||
|                     { |  | ||||||
|                         try { |  | ||||||
|                             if( !open ) throw new LuaException( "attempt to use a closed file" ); |  | ||||||
|                             writer.flush(); |  | ||||||
|                             return null; |  | ||||||
|                         } catch ( IOException e ) { |  | ||||||
|                             return null; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     default: |  | ||||||
|                     { |  | ||||||
|                         assert( false ); |  | ||||||
|                         return null; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } }; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     private static Object[] wrapInputStream( final IMountedFileBinary reader ) |  | ||||||
|     { |  | ||||||
|          |  | ||||||
|         return new Object[] { new ILuaObject() { |  | ||||||
|             private boolean open = true; |  | ||||||
|  |  | ||||||
|             @Nonnull |  | ||||||
|             @Override |  | ||||||
|             public String[] getMethodNames() { |  | ||||||
|                 return new String[] { |  | ||||||
|                         "read", |  | ||||||
|                         "close" |  | ||||||
|                     }; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             @Override |  | ||||||
|             public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args) throws LuaException { |  | ||||||
|                 switch( method ) |  | ||||||
|                 { |  | ||||||
|                     case 0: |  | ||||||
|                     { |  | ||||||
|                         // read |  | ||||||
|                         if( !open ) throw new LuaException( "attempt to use a closed file" ); |  | ||||||
|                         try { |  | ||||||
|                             int b = reader.read(); |  | ||||||
|                             if( b != -1 ) { |  | ||||||
|                                 return new Object[] { b }; |  | ||||||
|                             } else { |  | ||||||
|                                 return null; |  | ||||||
|                             } |  | ||||||
|                         } catch( IOException e ) { |  | ||||||
|                             return null; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     case 1: |  | ||||||
|                     { |  | ||||||
|                         //close |  | ||||||
|                         try { |  | ||||||
|                             reader.close(); |  | ||||||
|                             open = false; |  | ||||||
|                             return null; |  | ||||||
|                         } catch( IOException e ) { |  | ||||||
|                             return null; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     default: |  | ||||||
|                     { |  | ||||||
|                         assert( false ); |  | ||||||
|                         return null; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }}; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private static Object[] wrapOutputStream( final IMountedFileBinary writer ) |  | ||||||
|     {         |  | ||||||
|          |  | ||||||
|         return new Object[] { new ILuaObject() { |  | ||||||
|             private boolean open = true; |  | ||||||
|  |  | ||||||
|             @Nonnull |  | ||||||
|             @Override |  | ||||||
|             public String[] getMethodNames() { |  | ||||||
|                 return new String[] { |  | ||||||
|                         "write", |  | ||||||
|                         "close", |  | ||||||
|                         "flush" |  | ||||||
|                     }; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             @Override |  | ||||||
|             public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args) throws LuaException { |  | ||||||
|                 switch( method ) |  | ||||||
|                 { |  | ||||||
|                     case 0: |  | ||||||
|                     { |  | ||||||
|                         // write |  | ||||||
|                         if( !open ) throw new LuaException( "attempt to use a closed file" ); |  | ||||||
|                         try { |  | ||||||
|                             if( args.length > 0 && args[0] instanceof Number ) |  | ||||||
|                             { |  | ||||||
|                                 int number = ((Number)args[0]).intValue(); |  | ||||||
|                                 writer.write( number ); |  | ||||||
|                             } |  | ||||||
|                             return null; |  | ||||||
|                         } catch( IOException e ) { |  | ||||||
|                             throw new LuaException(e.getMessage()); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     case 1: |  | ||||||
|                     { |  | ||||||
|                         //close |  | ||||||
|                         try { |  | ||||||
|                             writer.close(); |  | ||||||
|                             open = false; |  | ||||||
|                             return null; |  | ||||||
|                         } catch( IOException e ) { |  | ||||||
|                             return null; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     case 2: |  | ||||||
|                     { |  | ||||||
|                         if( !open ) throw new LuaException( "attempt to use a closed file" ); |  | ||||||
|                         try { |  | ||||||
|                             writer.flush(); |  | ||||||
|                             return null; |  | ||||||
|                         } catch ( IOException e ) { |  | ||||||
|                             return null; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     default: |  | ||||||
|                     { |  | ||||||
|                         assert( false ); |  | ||||||
|                         return null; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }}; |  | ||||||
|     }     |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,10 +9,11 @@ package dan200.computercraft.core.apis; | |||||||
| import dan200.computercraft.api.lua.ILuaContext; | import dan200.computercraft.api.lua.ILuaContext; | ||||||
| import dan200.computercraft.api.lua.ILuaObject; | import dan200.computercraft.api.lua.ILuaObject; | ||||||
| import dan200.computercraft.api.lua.LuaException; | import dan200.computercraft.api.lua.LuaException; | ||||||
|  | import dan200.computercraft.core.apis.handles.BinaryInputHandle; | ||||||
|  | import dan200.computercraft.core.apis.handles.EncodedInputHandle; | ||||||
|  |  | ||||||
| import javax.annotation.Nonnull; | import javax.annotation.Nonnull; | ||||||
| import java.io.BufferedReader; | import java.io.InputStream; | ||||||
| import java.io.IOException; |  | ||||||
| import java.util.*; | import java.util.*; | ||||||
|  |  | ||||||
| public class HTTPAPI implements ILuaAPI | public class HTTPAPI implements ILuaAPI | ||||||
| @@ -52,15 +53,21 @@ public class HTTPAPI implements ILuaAPI | |||||||
|                     final String url = h.getURL(); |                     final String url = h.getURL(); | ||||||
|                     if( h.wasSuccessful() ) { |                     if( h.wasSuccessful() ) { | ||||||
|                         // Queue the "http_success" event |                         // Queue the "http_success" event | ||||||
|                         final BufferedReader contents = h.getContents(); |                         InputStream contents = h.getContents(); | ||||||
|                         final Object result = wrapBufferedReader( contents, h.getResponseCode(), h.getResponseHeaders() ); |                         Object result = wrapStream( | ||||||
|  |                             h.isBinary() ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, h.getEncoding() ), | ||||||
|  |                             h.getResponseCode(), h.getResponseHeaders() | ||||||
|  |                         ); | ||||||
|                         m_apiEnvironment.queueEvent( "http_success", new Object[] { url, result } ); |                         m_apiEnvironment.queueEvent( "http_success", new Object[] { url, result } ); | ||||||
|                     } else { |                     } else { | ||||||
|                         // Queue the "http_failure" event |                         // Queue the "http_failure" event | ||||||
|                         BufferedReader contents = h.getContents(); |                         InputStream contents = h.getContents(); | ||||||
|                         Object result = null; |                         Object result = null; | ||||||
|                         if( contents != null ) { |                         if( contents != null ) { | ||||||
|                             result = wrapBufferedReader( contents, h.getResponseCode(), h.getResponseHeaders() ); |                             result = wrapStream( | ||||||
|  |                                 h.isBinary() ? new BinaryInputHandle( contents ) : new EncodedInputHandle( contents, h.getEncoding() ), | ||||||
|  |                                 h.getResponseCode(), h.getResponseHeaders() | ||||||
|  |                             ); | ||||||
|                         } |                         } | ||||||
|                         m_apiEnvironment.queueEvent( "http_failure", new Object[]{ url, "Could not connect", result } ); |                         m_apiEnvironment.queueEvent( "http_failure", new Object[]{ url, "Could not connect", result } ); | ||||||
|                     } |                     } | ||||||
| @@ -70,79 +77,39 @@ public class HTTPAPI implements ILuaAPI | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private static ILuaObject wrapBufferedReader( final BufferedReader reader, final int responseCode, final Map<String, String> responseHeaders ) |     private static ILuaObject wrapStream( final ILuaObject reader, final int responseCode, final Map<String, String> responseHeaders ) | ||||||
|     { |     { | ||||||
|         return new ILuaObject() { |         String[] oldMethods = reader.getMethodNames(); | ||||||
|             private boolean open = true; |         final int methodOffset = oldMethods.length; | ||||||
|  |  | ||||||
|  |         final String[] newMethods = Arrays.copyOf( oldMethods, oldMethods.length + 2 ); | ||||||
|  |         newMethods[ methodOffset + 0 ] = "getResponseCode"; | ||||||
|  |         newMethods[ methodOffset + 1 ] = "getResponseHeaders"; | ||||||
|  |  | ||||||
|  |         return new ILuaObject() | ||||||
|  |         { | ||||||
|             @Nonnull |             @Nonnull | ||||||
|             @Override |             @Override | ||||||
|             public String[] getMethodNames() |             public String[] getMethodNames() | ||||||
|             { |             { | ||||||
|                 return new String[] { |                 return newMethods; | ||||||
|                     "readLine", |  | ||||||
|                     "readAll", |  | ||||||
|                     "close", |  | ||||||
|                     "getResponseCode", |  | ||||||
|                     "getResponseHeaders", |  | ||||||
|                 }; |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             @Override |             @Override | ||||||
|             public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException |             public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException, InterruptedException | ||||||
|             { |             { | ||||||
|                 switch( method ) |                 if( method < methodOffset ) | ||||||
|  |                 { | ||||||
|  |                     return reader.callMethod( context, method, args ); | ||||||
|  |                 } | ||||||
|  |                 switch( method - methodOffset ) | ||||||
|                 { |                 { | ||||||
|                     case 0: |                     case 0: | ||||||
|                     { |  | ||||||
|                         // readLine |  | ||||||
|                         if( !open ) throw new LuaException( "attempt to use a closed response" ); |  | ||||||
|                         try { |  | ||||||
|                             String line = reader.readLine(); |  | ||||||
|                             if( line != null ) { |  | ||||||
|                                 return new Object[] { line }; |  | ||||||
|                             } else { |  | ||||||
|                                 return null; |  | ||||||
|                             } |  | ||||||
|                         } catch( IOException e ) { |  | ||||||
|                             return null; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     case 1: |  | ||||||
|                     { |  | ||||||
|                         // readAll |  | ||||||
|                         if( !open ) throw new LuaException( "attempt to use a closed response" ); |  | ||||||
|                         try { |  | ||||||
|                             StringBuilder result = new StringBuilder( "" ); |  | ||||||
|                             String line = reader.readLine(); |  | ||||||
|                             while( line != null ) { |  | ||||||
|                                 result.append( line ); |  | ||||||
|                                 line = reader.readLine(); |  | ||||||
|                                 if( line != null ) { |  | ||||||
|                                     result.append( "\n" ); |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                             return new Object[] { result.toString() }; |  | ||||||
|                         } catch( IOException e ) { |  | ||||||
|                             return null; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     case 2: |  | ||||||
|                     { |  | ||||||
|                         // close |  | ||||||
|                         try { |  | ||||||
|                             reader.close(); |  | ||||||
|                             open = false; |  | ||||||
|                             return null; |  | ||||||
|                         } catch( IOException e ) { |  | ||||||
|                             return null; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     case 3: |  | ||||||
|                     { |                     { | ||||||
|                         // getResponseCode |                         // getResponseCode | ||||||
|                         return new Object[] { responseCode }; |                         return new Object[] { responseCode }; | ||||||
|                     } |                     } | ||||||
|                     case 4: |                     case 1: | ||||||
|                     { |                     { | ||||||
|                         // getResponseHeaders |                         // getResponseHeaders | ||||||
|                         return new Object[] { responseHeaders }; |                         return new Object[] { responseHeaders }; | ||||||
| @@ -217,10 +184,17 @@ public class HTTPAPI implements ILuaAPI | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                  |                  | ||||||
|  |                 // Get binary | ||||||
|  |                 boolean binary = false; | ||||||
|  |                 if( args.length >= 4 ) | ||||||
|  |                 { | ||||||
|  |                     binary = args[ 3 ] != null && !args[ 3 ].equals( Boolean.FALSE ); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 // Make the request |                 // Make the request | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     HTTPRequest request = new HTTPRequest( urlString, postString, headers ); |                     HTTPRequest request = new HTTPRequest( urlString, postString, headers, binary ); | ||||||
|                     synchronized( m_httpRequests ) |                     synchronized( m_httpRequests ) | ||||||
|                     { |                     { | ||||||
|                         m_httpRequests.add( request ); |                         m_httpRequests.add( request ); | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ | |||||||
| package dan200.computercraft.core.apis; | package dan200.computercraft.core.apis; | ||||||
|  |  | ||||||
| import com.google.common.base.Joiner; | import com.google.common.base.Joiner; | ||||||
|  | import com.google.common.io.ByteStreams; | ||||||
| import dan200.computercraft.ComputerCraft; | import dan200.computercraft.ComputerCraft; | ||||||
|  |  | ||||||
| import java.io.*; | import java.io.*; | ||||||
| @@ -60,11 +61,12 @@ public class HTTPRequest | |||||||
|         return url; |         return url; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public HTTPRequest( String url, final String postText, final Map<String, String> headers ) throws HTTPRequestException |     public HTTPRequest( String url, final String postText, final Map<String, String> headers, boolean binary ) throws HTTPRequestException | ||||||
|     { |     { | ||||||
|         // Parse the URL |         // Parse the URL | ||||||
|         m_urlString = url; |         m_urlString = url; | ||||||
|         m_url = checkURL( m_urlString ); |         m_url = checkURL( m_urlString ); | ||||||
|  |         m_binary = binary; | ||||||
|  |  | ||||||
|         // Start the thread |         // Start the thread | ||||||
|         m_cancelled = false; |         m_cancelled = false; | ||||||
| @@ -136,53 +138,9 @@ public class HTTPRequest | |||||||
|                         is = connection.getErrorStream(); |                         is = connection.getErrorStream(); | ||||||
|                         responseSuccess = false; |                         responseSuccess = false; | ||||||
|                     } |                     } | ||||||
|                     InputStreamReader isr; |  | ||||||
|                     try |  | ||||||
|                     { |  | ||||||
|                         String contentEncoding = connection.getContentEncoding(); |  | ||||||
|                         if( contentEncoding != null ) |  | ||||||
|                         { |  | ||||||
|                             try |  | ||||||
|                             { |  | ||||||
|                                 isr = new InputStreamReader( is, contentEncoding ); |  | ||||||
|                             } |  | ||||||
|                             catch( UnsupportedEncodingException e ) |  | ||||||
|                             { |  | ||||||
|                                 isr = new InputStreamReader( is, "UTF-8" ); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                         else |  | ||||||
|                         { |  | ||||||
|                             isr = new InputStreamReader( is, "UTF-8" ); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     catch( UnsupportedEncodingException e ) |  | ||||||
|                     { |  | ||||||
|                         isr = new InputStreamReader( is ); |  | ||||||
|                     } |  | ||||||
|                      |                      | ||||||
|                     // Download the contents |                     byte[] result = ByteStreams.toByteArray( is ); | ||||||
|                     BufferedReader reader = new BufferedReader( isr ); |                     is.close(); | ||||||
|                     StringBuilder result = new StringBuilder(); |  | ||||||
|                     while( true ) |  | ||||||
|                     { |  | ||||||
|                         synchronized( m_lock ) |  | ||||||
|                         { |  | ||||||
|                             if( m_cancelled ) |  | ||||||
|                             { |  | ||||||
|                                 break; |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         String line = reader.readLine(); |  | ||||||
|                         if( line == null ) |  | ||||||
|                         { |  | ||||||
|                             break; |  | ||||||
|                         } |  | ||||||
|                         result.append( line ); |  | ||||||
|                         result.append( '\n' ); |  | ||||||
|                     } |  | ||||||
|                     reader.close(); |  | ||||||
|                      |                      | ||||||
|                     synchronized( m_lock ) |                     synchronized( m_lock ) | ||||||
|                     { |                     { | ||||||
| @@ -198,8 +156,9 @@ public class HTTPRequest | |||||||
|                             // We completed |                             // We completed | ||||||
|                             m_complete = true; |                             m_complete = true; | ||||||
|                             m_success = responseSuccess; |                             m_success = responseSuccess; | ||||||
|                             m_result = result.toString(); |                             m_result = result; | ||||||
|                             m_responseCode = connection.getResponseCode(); |                             m_responseCode = connection.getResponseCode(); | ||||||
|  |                             m_encoding = connection.getContentEncoding(); | ||||||
|  |  | ||||||
|                             Joiner joiner = Joiner.on( ',' ); |                             Joiner joiner = Joiner.on( ',' ); | ||||||
|                             Map<String, String> headers = m_responseHeaders = new HashMap<String, String>(); |                             Map<String, String> headers = m_responseHeaders = new HashMap<String, String>(); | ||||||
| @@ -265,19 +224,28 @@ public class HTTPRequest | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public BufferedReader getContents() |     public boolean isBinary() | ||||||
|     { |     { | ||||||
|         String result; |         return m_binary; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     public InputStream getContents() | ||||||
|  |     { | ||||||
|  |         byte[] result; | ||||||
|         synchronized(m_lock) { |         synchronized(m_lock) { | ||||||
|             result = m_result; |             result = m_result; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         if( result != null ) { |         if( result != null ) { | ||||||
|             return new BufferedReader( new StringReader( result ) ); |             return new ByteArrayInputStream( result ); | ||||||
|         } |         } | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     public String getEncoding() { | ||||||
|  |         return m_encoding; | ||||||
|  |     } | ||||||
|  |      | ||||||
|     private final Object m_lock = new Object(); |     private final Object m_lock = new Object(); | ||||||
|     private final URL m_url; |     private final URL m_url; | ||||||
|     private final String m_urlString; |     private final String m_urlString; | ||||||
| @@ -285,7 +253,9 @@ public class HTTPRequest | |||||||
|     private boolean m_complete; |     private boolean m_complete; | ||||||
|     private boolean m_cancelled; |     private boolean m_cancelled; | ||||||
|     private boolean m_success; |     private boolean m_success; | ||||||
|     private String m_result; |     private String m_encoding; | ||||||
|  |     private byte[] m_result; | ||||||
|  |     private boolean m_binary; | ||||||
|     private int m_responseCode; |     private int m_responseCode; | ||||||
|     private Map<String, String> m_responseHeaders; |     private Map<String, String> m_responseHeaders; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,93 @@ | |||||||
|  | package dan200.computercraft.core.apis.handles; | ||||||
|  |  | ||||||
|  | import com.google.common.io.ByteStreams; | ||||||
|  | import dan200.computercraft.api.lua.ILuaContext; | ||||||
|  | import dan200.computercraft.api.lua.LuaException; | ||||||
|  |  | ||||||
|  | import javax.annotation.Nonnull; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.util.Arrays; | ||||||
|  |  | ||||||
|  | public class BinaryInputHandle extends HandleGeneric | ||||||
|  | { | ||||||
|  |     private final InputStream m_stream; | ||||||
|  |  | ||||||
|  |     public BinaryInputHandle( InputStream reader ) | ||||||
|  |     { | ||||||
|  |         super( reader ); | ||||||
|  |         this.m_stream = reader; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Nonnull | ||||||
|  |     @Override | ||||||
|  |     public String[] getMethodNames() | ||||||
|  |     { | ||||||
|  |         return new String[] { | ||||||
|  |             "read", | ||||||
|  |             "readAll", | ||||||
|  |             "close", | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException | ||||||
|  |     { | ||||||
|  |         switch( method ) | ||||||
|  |         { | ||||||
|  |             case 0: | ||||||
|  |                 // read | ||||||
|  |                 checkOpen(); | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     if( args.length > 0 && args[ 0 ] != null ) | ||||||
|  |                     { | ||||||
|  |                         if( !(args[ 0 ] instanceof Number) ) | ||||||
|  |                         { | ||||||
|  |                             throw new LuaException( "Expected number" ); | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         int count = ((Number) args[ 0 ]).intValue(); | ||||||
|  |  | ||||||
|  |                         if( count <= 0 || count >= 1024 * 16 ) | ||||||
|  |                         { | ||||||
|  |                             throw new LuaException( "Count out of range" ); | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         byte[] bytes = new byte[ count ]; | ||||||
|  |                         count = m_stream.read( bytes ); | ||||||
|  |                         if( count < 0 ) return null; | ||||||
|  |                         if( count < bytes.length ) bytes = Arrays.copyOf( bytes, count ); | ||||||
|  |                         return new Object[] { bytes }; | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         int b = m_stream.read(); | ||||||
|  |                         return b == -1 ? null : new Object[] { b }; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 catch( IOException e ) | ||||||
|  |                 { | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |             case 1: | ||||||
|  |                 // readAll | ||||||
|  |                 checkOpen(); | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     byte[] out = ByteStreams.toByteArray( m_stream ); | ||||||
|  |                     return out == null ? null : new Object[] { out }; | ||||||
|  |                 } | ||||||
|  |                 catch( IOException e ) | ||||||
|  |                 { | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |             case 2: | ||||||
|  |                 //close | ||||||
|  |                 close(); | ||||||
|  |                 return null; | ||||||
|  |             default: | ||||||
|  |                 return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,82 @@ | |||||||
|  | package dan200.computercraft.core.apis.handles; | ||||||
|  |  | ||||||
|  | import dan200.computercraft.api.lua.ILuaContext; | ||||||
|  | import dan200.computercraft.api.lua.LuaException; | ||||||
|  | import dan200.computercraft.shared.util.StringUtil; | ||||||
|  |  | ||||||
|  | import javax.annotation.Nonnull; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.OutputStream; | ||||||
|  |  | ||||||
|  | public class BinaryOutputHandle extends HandleGeneric | ||||||
|  | { | ||||||
|  |     private final OutputStream m_writer; | ||||||
|  |  | ||||||
|  |     public BinaryOutputHandle( OutputStream writer ) | ||||||
|  |     { | ||||||
|  |         super( writer ); | ||||||
|  |         this.m_writer = writer; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Nonnull | ||||||
|  |     @Override | ||||||
|  |     public String[] getMethodNames() | ||||||
|  |     { | ||||||
|  |         return new String[] { | ||||||
|  |             "write", | ||||||
|  |             "flush", | ||||||
|  |             "close", | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException | ||||||
|  |     { | ||||||
|  |         switch( method ) | ||||||
|  |         { | ||||||
|  |             case 0: | ||||||
|  |                 // write | ||||||
|  |                 checkOpen(); | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     if( args.length > 0 && args[ 0 ] instanceof Number ) | ||||||
|  |                     { | ||||||
|  |                         int number = ((Number) args[ 0 ]).intValue(); | ||||||
|  |                         m_writer.write( number ); | ||||||
|  |                     } | ||||||
|  |                     else if( args.length > 0 && args[ 0 ] instanceof String ) | ||||||
|  |                     { | ||||||
|  |                         String value = (String) args[ 0 ]; | ||||||
|  |                         m_writer.write( StringUtil.encodeString( value ) ); | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         throw new LuaException( "Expected number" ); | ||||||
|  |                     } | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |                 catch( IOException e ) | ||||||
|  |                 { | ||||||
|  |                     throw new LuaException( e.getMessage() ); | ||||||
|  |                 } | ||||||
|  |             case 1: | ||||||
|  |                 // flush | ||||||
|  |                 checkOpen(); | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     m_writer.flush(); | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |                 catch( IOException e ) | ||||||
|  |                 { | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |             case 2: | ||||||
|  |                 //close | ||||||
|  |                 close(); | ||||||
|  |                 return null; | ||||||
|  |             default: | ||||||
|  |                 return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,105 @@ | |||||||
|  | package dan200.computercraft.core.apis.handles; | ||||||
|  |  | ||||||
|  | import dan200.computercraft.api.lua.ILuaContext; | ||||||
|  | import dan200.computercraft.api.lua.LuaException; | ||||||
|  |  | ||||||
|  | import javax.annotation.Nonnull; | ||||||
|  | import java.io.*; | ||||||
|  |  | ||||||
|  | public class EncodedInputHandle extends HandleGeneric | ||||||
|  | { | ||||||
|  |     private final BufferedReader m_reader; | ||||||
|  |  | ||||||
|  |     public EncodedInputHandle( BufferedReader reader ) | ||||||
|  |     { | ||||||
|  |         super( reader ); | ||||||
|  |         this.m_reader = reader; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public EncodedInputHandle( InputStream stream ) | ||||||
|  |     { | ||||||
|  |         this( stream, "UTF-8" ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public EncodedInputHandle( InputStream stream, String encoding ) | ||||||
|  |     { | ||||||
|  |         super( stream ); | ||||||
|  |         if( encoding == null ) encoding = "UTF-8"; | ||||||
|  |         InputStreamReader streamReader; | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             streamReader = new InputStreamReader( stream, encoding ); | ||||||
|  |         } | ||||||
|  |         catch( UnsupportedEncodingException e ) | ||||||
|  |         { | ||||||
|  |             streamReader = new InputStreamReader( stream ); | ||||||
|  |         } | ||||||
|  |         this.m_reader = new BufferedReader( streamReader ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Nonnull | ||||||
|  |     @Override | ||||||
|  |     public String[] getMethodNames() | ||||||
|  |     { | ||||||
|  |         return new String[] { | ||||||
|  |             "readLine", | ||||||
|  |             "readAll", | ||||||
|  |             "close", | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException | ||||||
|  |     { | ||||||
|  |         switch( method ) | ||||||
|  |         { | ||||||
|  |             case 0: | ||||||
|  |                 // readLine | ||||||
|  |                 checkOpen(); | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     String line = m_reader.readLine(); | ||||||
|  |                     if( line != null ) | ||||||
|  |                     { | ||||||
|  |                         return new Object[] { line }; | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         return null; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 catch( IOException e ) | ||||||
|  |                 { | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |             case 1: | ||||||
|  |                 // readAll | ||||||
|  |                 checkOpen(); | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     StringBuilder result = new StringBuilder( "" ); | ||||||
|  |                     String line = m_reader.readLine(); | ||||||
|  |                     while( line != null ) | ||||||
|  |                     { | ||||||
|  |                         result.append( line ); | ||||||
|  |                         line = m_reader.readLine(); | ||||||
|  |                         if( line != null ) | ||||||
|  |                         { | ||||||
|  |                             result.append( "\n" ); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     return new Object[] { result.toString() }; | ||||||
|  |                 } | ||||||
|  |                 catch( IOException e ) | ||||||
|  |                 { | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |             case 2: | ||||||
|  |                 // close | ||||||
|  |                 close(); | ||||||
|  |                 return null; | ||||||
|  |             default: | ||||||
|  |                 return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,124 @@ | |||||||
|  | package dan200.computercraft.core.apis.handles; | ||||||
|  |  | ||||||
|  | import dan200.computercraft.api.lua.ILuaContext; | ||||||
|  | import dan200.computercraft.api.lua.LuaException; | ||||||
|  |  | ||||||
|  | import javax.annotation.Nonnull; | ||||||
|  | import java.io.*; | ||||||
|  |  | ||||||
|  | public class EncodedOutputHandle extends HandleGeneric | ||||||
|  | { | ||||||
|  |     private final BufferedWriter m_writer; | ||||||
|  |  | ||||||
|  |     public EncodedOutputHandle( BufferedWriter writer ) | ||||||
|  |     { | ||||||
|  |         super( writer ); | ||||||
|  |         this.m_writer = writer; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public EncodedOutputHandle( OutputStream stream ) | ||||||
|  |     { | ||||||
|  |         this( stream, "UTF-8" ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public EncodedOutputHandle( OutputStream stream, String encoding ) | ||||||
|  |     { | ||||||
|  |         super( stream ); | ||||||
|  |         if( encoding == null ) encoding = "UTF-8"; | ||||||
|  |         OutputStreamWriter streamWriter; | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             streamWriter = new OutputStreamWriter( stream, encoding ); | ||||||
|  |         } | ||||||
|  |         catch( UnsupportedEncodingException e ) | ||||||
|  |         { | ||||||
|  |             streamWriter = new OutputStreamWriter( stream ); | ||||||
|  |         } | ||||||
|  |         this.m_writer = new BufferedWriter( streamWriter ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Nonnull | ||||||
|  |     @Override | ||||||
|  |     public String[] getMethodNames() | ||||||
|  |     { | ||||||
|  |         return new String[] { | ||||||
|  |             "write", | ||||||
|  |             "writeLine", | ||||||
|  |             "flush", | ||||||
|  |             "close", | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] args ) throws LuaException | ||||||
|  |     { | ||||||
|  |         switch( method ) | ||||||
|  |         { | ||||||
|  |             case 0: | ||||||
|  |             { | ||||||
|  |                 // write | ||||||
|  |                 checkOpen(); | ||||||
|  |                 String text; | ||||||
|  |                 if( args.length > 0 && args[ 0 ] != null ) | ||||||
|  |                 { | ||||||
|  |                     text = args[ 0 ].toString(); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     text = ""; | ||||||
|  |                 } | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     m_writer.write( text, 0, text.length() ); | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |                 catch( IOException e ) | ||||||
|  |                 { | ||||||
|  |                     throw new LuaException( e.getMessage() ); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             case 1: | ||||||
|  |             { | ||||||
|  |                 // writeLine | ||||||
|  |                 checkOpen(); | ||||||
|  |                 String text; | ||||||
|  |                 if( args.length > 0 && args[ 0 ] != null ) | ||||||
|  |                 { | ||||||
|  |                     text = args[ 0 ].toString(); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     text = ""; | ||||||
|  |                 } | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     m_writer.write( text, 0, text.length() ); | ||||||
|  |                     m_writer.newLine(); | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |                 catch( IOException e ) | ||||||
|  |                 { | ||||||
|  |                     throw new LuaException( e.getMessage() ); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             case 2: | ||||||
|  |                 // flush | ||||||
|  |                 checkOpen(); | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     m_writer.flush(); | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |                 catch( IOException e ) | ||||||
|  |                 { | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |             case 3: | ||||||
|  |                 // close | ||||||
|  |                 close(); | ||||||
|  |                 return null; | ||||||
|  |             default: | ||||||
|  |                 return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,35 @@ | |||||||
|  | package dan200.computercraft.core.apis.handles; | ||||||
|  |  | ||||||
|  | import dan200.computercraft.api.lua.ILuaObject; | ||||||
|  | import dan200.computercraft.api.lua.LuaException; | ||||||
|  |  | ||||||
|  | import java.io.Closeable; | ||||||
|  | import java.io.IOException; | ||||||
|  |  | ||||||
|  | public abstract class HandleGeneric implements ILuaObject | ||||||
|  | { | ||||||
|  |     protected final Closeable m_closable; | ||||||
|  |     protected boolean m_open = true; | ||||||
|  |  | ||||||
|  |     public HandleGeneric( Closeable m_closable ) | ||||||
|  |     { | ||||||
|  |         this.m_closable = m_closable; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected void checkOpen() throws LuaException | ||||||
|  |     { | ||||||
|  |         if( !m_open ) throw new LuaException( "attempt to use a closed file" ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected void close() | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             m_closable.close(); | ||||||
|  |             m_open = false; | ||||||
|  |         } | ||||||
|  |         catch( IOException ignored ) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -9,6 +9,7 @@ package dan200.computercraft.core.filesystem; | |||||||
| import dan200.computercraft.ComputerCraft; | import dan200.computercraft.ComputerCraft; | ||||||
| import dan200.computercraft.api.filesystem.IMount; | import dan200.computercraft.api.filesystem.IMount; | ||||||
| import dan200.computercraft.api.filesystem.IWritableMount; | import dan200.computercraft.api.filesystem.IWritableMount; | ||||||
|  | import net.minecraftforge.fml.common.FMLLog; | ||||||
|  |  | ||||||
| import java.io.*; | import java.io.*; | ||||||
| import java.util.*; | import java.util.*; | ||||||
| @@ -291,7 +292,7 @@ public class FileSystem | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     private final Map<String, MountWrapper> m_mounts = new HashMap<String, MountWrapper>(); |     private final Map<String, MountWrapper> m_mounts = new HashMap<String, MountWrapper>(); | ||||||
|     private final Set<IMountedFile> m_openFiles = Collections.newSetFromMap( new WeakHashMap<IMountedFile, Boolean>() ); |     private final Set<Closeable> m_openFiles = Collections.newSetFromMap( new WeakHashMap<Closeable, Boolean>() ); | ||||||
|      |      | ||||||
|     public FileSystem( String rootLabel, IMount rootMount ) throws FileSystemException |     public FileSystem( String rootLabel, IMount rootMount ) throws FileSystemException | ||||||
|     { |     { | ||||||
| @@ -308,7 +309,7 @@ public class FileSystem | |||||||
|         // Close all dangling open files |         // Close all dangling open files | ||||||
|         synchronized( m_openFiles ) |         synchronized( m_openFiles ) | ||||||
|         { |         { | ||||||
|             for(IMountedFile file : m_openFiles) |             for( Closeable file : m_openFiles ) | ||||||
|             { |             { | ||||||
|                 try { |                 try { | ||||||
|                     file.close(); |                     file.close(); | ||||||
| @@ -647,7 +648,7 @@ public class FileSystem | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private synchronized <T extends IMountedFile> T openFile(T file, Closeable handle) throws FileSystemException |     private synchronized <T> T openFile( T file, Closeable handle ) throws FileSystemException | ||||||
|     { |     { | ||||||
|         synchronized( m_openFiles ) |         synchronized( m_openFiles ) | ||||||
|         { |         { | ||||||
| @@ -665,195 +666,40 @@ public class FileSystem | |||||||
|                 throw new FileSystemException("Too many files already open"); |                 throw new FileSystemException("Too many files already open"); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             m_openFiles.add( file ); |             m_openFiles.add( handle ); | ||||||
|             return file; |             return file; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private synchronized void closeFile( IMountedFile file, Closeable handle ) throws IOException |     private synchronized void closeFile( Closeable handle ) throws IOException | ||||||
|     { |     { | ||||||
|         synchronized( m_openFiles ) |         synchronized( m_openFiles ) | ||||||
|         { |         { | ||||||
|             m_openFiles.remove( file ); |             m_openFiles.remove( handle ); | ||||||
|             if( handle != null ) |             handle.close(); | ||||||
|             { |  | ||||||
|                 handle.close(); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     public synchronized IMountedFileNormal openForRead( String path ) throws FileSystemException |     public synchronized InputStream openForRead( String path ) throws FileSystemException | ||||||
|     { |     { | ||||||
|         path = sanitizePath ( path ); |         path = sanitizePath ( path ); | ||||||
|         MountWrapper mount = getMount( path ); |         MountWrapper mount = getMount( path ); | ||||||
|         InputStream stream = mount.openForRead( path ); |         InputStream stream = mount.openForRead( path ); | ||||||
|         if( stream != null ) |         if( stream != null ) | ||||||
|         { |         { | ||||||
|             InputStreamReader isr; |             return openFile( new ClosingInputStream( stream ), stream ); | ||||||
|             try |  | ||||||
|             { |  | ||||||
|                 isr = new InputStreamReader( stream, "UTF-8" ); |  | ||||||
|             } |  | ||||||
|             catch( UnsupportedEncodingException e ) |  | ||||||
|             { |  | ||||||
|                 isr = new InputStreamReader( stream ); |  | ||||||
|             } |  | ||||||
|             final BufferedReader reader = new BufferedReader( isr ); |  | ||||||
|             IMountedFileNormal file = new IMountedFileNormal() |  | ||||||
|             { |  | ||||||
|                 @Override |  | ||||||
|                 public String readLine() throws IOException |  | ||||||
|                 { |  | ||||||
|                     return reader.readLine(); |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 @Override |  | ||||||
|                 public void write(String s, int off, int len, boolean newLine) throws IOException |  | ||||||
|                 { |  | ||||||
|                     throw new UnsupportedOperationException(); |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 @Override |  | ||||||
|                 public void close() throws IOException |  | ||||||
|                 { |  | ||||||
|                     closeFile( this, reader ); |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 @Override |  | ||||||
|                 public void flush() throws IOException |  | ||||||
|                 { |  | ||||||
|                     throw new UnsupportedOperationException(); |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
|             return openFile( file, reader ); |  | ||||||
|         } |         } | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public synchronized IMountedFileNormal openForWrite( String path, boolean append ) throws FileSystemException |     public synchronized OutputStream openForWrite( String path, boolean append ) throws FileSystemException | ||||||
|     { |     { | ||||||
|         path = sanitizePath ( path ); |         path = sanitizePath ( path ); | ||||||
|         MountWrapper mount = getMount( path ); |         MountWrapper mount = getMount( path ); | ||||||
|         OutputStream stream = append ? mount.openForAppend( path ) : mount.openForWrite( path ); |         OutputStream stream = append ? mount.openForAppend( path ) : mount.openForWrite( path ); | ||||||
|         if( stream != null ) |         if( stream != null ) | ||||||
|         { |         { | ||||||
|             OutputStreamWriter osw; |             return openFile( new ClosingOutputStream( stream ), stream ); | ||||||
|             try |  | ||||||
|             { |  | ||||||
|                 osw = new OutputStreamWriter( stream, "UTF-8" ); |  | ||||||
|             } |  | ||||||
|             catch( UnsupportedEncodingException e ) |  | ||||||
|             { |  | ||||||
|                 osw = new OutputStreamWriter( stream ); |  | ||||||
|             } |  | ||||||
|             final BufferedWriter writer = new BufferedWriter( osw ); |  | ||||||
|             IMountedFileNormal file = new IMountedFileNormal() |  | ||||||
|             { |  | ||||||
|                 @Override |  | ||||||
|                 public String readLine() throws IOException |  | ||||||
|                 { |  | ||||||
|                     throw new UnsupportedOperationException(); |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 @Override |  | ||||||
|                 public void write( String s, int off, int len, boolean newLine ) throws IOException |  | ||||||
|                 { |  | ||||||
|                     writer.write( s, off, len ); |  | ||||||
|                     if( newLine ) |  | ||||||
|                     { |  | ||||||
|                         writer.newLine(); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 @Override |  | ||||||
|                 public void close() throws IOException |  | ||||||
|                 { |  | ||||||
|                     closeFile( this, writer ); |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 @Override |  | ||||||
|                 public void flush() throws IOException |  | ||||||
|                 { |  | ||||||
|                     writer.flush(); |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
|             return openFile( file, writer ); |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public synchronized IMountedFileBinary openForBinaryRead( String path ) throws FileSystemException |  | ||||||
|     { |  | ||||||
|         path = sanitizePath ( path ); |  | ||||||
|         MountWrapper mount = getMount( path ); |  | ||||||
|         final InputStream stream = mount.openForRead( path ); |  | ||||||
|         if( stream != null ) |  | ||||||
|         { |  | ||||||
|             IMountedFileBinary file = new IMountedFileBinary() |  | ||||||
|             { |  | ||||||
|                 @Override |  | ||||||
|                 public int read() throws IOException |  | ||||||
|                 { |  | ||||||
|                     return stream.read(); |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 @Override |  | ||||||
|                 public void write(int i) throws IOException |  | ||||||
|                 { |  | ||||||
|                     throw new UnsupportedOperationException(); |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 @Override |  | ||||||
|                 public void close() throws IOException |  | ||||||
|                 { |  | ||||||
|                     closeFile( this, stream ); |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 @Override |  | ||||||
|                 public void flush() throws IOException |  | ||||||
|                 { |  | ||||||
|                     throw new UnsupportedOperationException(); |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
|             return openFile( file, stream ); |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public synchronized IMountedFileBinary openForBinaryWrite( String path, boolean append ) throws FileSystemException |  | ||||||
|     { |  | ||||||
|         path = sanitizePath ( path ); |  | ||||||
|         MountWrapper mount = getMount( path ); |  | ||||||
|         final OutputStream stream = append ? mount.openForAppend( path ) : mount.openForWrite( path ); |  | ||||||
|         if( stream != null ) |  | ||||||
|         { |  | ||||||
|             IMountedFileBinary file = new IMountedFileBinary() |  | ||||||
|             { |  | ||||||
|                 @Override |  | ||||||
|                 public int read() throws IOException |  | ||||||
|                 { |  | ||||||
|                     throw new UnsupportedOperationException(); |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 @Override |  | ||||||
|                 public void write(int i) throws IOException |  | ||||||
|                 { |  | ||||||
|                     stream.write(i); |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 @Override |  | ||||||
|                 public void close() throws IOException |  | ||||||
|                 { |  | ||||||
|                     closeFile( this, stream ); |  | ||||||
|                 } |  | ||||||
|                  |  | ||||||
|                 @Override |  | ||||||
|                 public void flush() throws IOException |  | ||||||
|                 { |  | ||||||
|                     stream.flush(); |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
|             return openFile( file, stream ); |  | ||||||
|         } |         } | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
| @@ -1010,4 +856,34 @@ public class FileSystem | |||||||
|             return local; |             return local; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private class ClosingInputStream extends FilterInputStream | ||||||
|  |     { | ||||||
|  |         protected ClosingInputStream( InputStream in ) | ||||||
|  |         { | ||||||
|  |             super( in ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public void close() throws IOException | ||||||
|  |         { | ||||||
|  |             super.close(); | ||||||
|  |             closeFile( in ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private class ClosingOutputStream extends FilterOutputStream | ||||||
|  |     { | ||||||
|  |         protected ClosingOutputStream( OutputStream out ) | ||||||
|  |         { | ||||||
|  |             super( out ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public void close() throws IOException | ||||||
|  |         { | ||||||
|  |             super.close(); | ||||||
|  |             closeFile( out ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,13 +0,0 @@ | |||||||
| /* |  | ||||||
|  * This file is part of ComputerCraft - http://www.computercraft.info |  | ||||||
|  * Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission. |  | ||||||
|  * Send enquiries to dratcliffe@gmail.com |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| package dan200.computercraft.core.filesystem; |  | ||||||
|  |  | ||||||
| import java.io.IOException; |  | ||||||
|  |  | ||||||
| public interface IMountedFile { |  | ||||||
|     void close() throws IOException; |  | ||||||
| } |  | ||||||
| @@ -1,16 +0,0 @@ | |||||||
| /* |  | ||||||
|  * This file is part of ComputerCraft - http://www.computercraft.info |  | ||||||
|  * Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission. |  | ||||||
|  * Send enquiries to dratcliffe@gmail.com |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| package dan200.computercraft.core.filesystem; |  | ||||||
|  |  | ||||||
| import java.io.IOException; |  | ||||||
|  |  | ||||||
| public interface IMountedFileBinary extends IMountedFile { |  | ||||||
|     int read() throws IOException; |  | ||||||
|     void write( int i ) throws IOException; |  | ||||||
|     void close() throws IOException; |  | ||||||
|     void flush() throws IOException; |  | ||||||
| } |  | ||||||
| @@ -1,16 +0,0 @@ | |||||||
| /* |  | ||||||
|  * This file is part of ComputerCraft - http://www.computercraft.info |  | ||||||
|  * Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission. |  | ||||||
|  * Send enquiries to dratcliffe@gmail.com |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| package dan200.computercraft.core.filesystem; |  | ||||||
|  |  | ||||||
| import java.io.IOException; |  | ||||||
|  |  | ||||||
| public interface IMountedFileNormal extends IMountedFile { |  | ||||||
|     String readLine() throws IOException; |  | ||||||
|     void write( String s, int off, int len, boolean newLine ) throws IOException; |  | ||||||
|     void close() throws IOException; |  | ||||||
|     void flush() throws IOException; |  | ||||||
| } |  | ||||||
| @@ -24,6 +24,7 @@ import org.luaj.vm2.lib.jse.JsePlatform; | |||||||
|  |  | ||||||
| import javax.annotation.Nonnull; | import javax.annotation.Nonnull; | ||||||
| import java.io.*; | import java.io.*; | ||||||
|  | import java.util.Arrays; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.IdentityHashMap; | import java.util.IdentityHashMap; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| @@ -520,6 +521,11 @@ public class LuaJLuaMachine implements ILuaMachine | |||||||
|             String s = object.toString(); |             String s = object.toString(); | ||||||
|             return LuaValue.valueOf( s ); |             return LuaValue.valueOf( s ); | ||||||
|         } |         } | ||||||
|  |         else if( object instanceof byte[] ) | ||||||
|  |         { | ||||||
|  |             byte[] b = (byte[]) object; | ||||||
|  |             return LuaValue.valueOf( Arrays.copyOf( b, b.length ) ); | ||||||
|  |         } | ||||||
|         else if( object instanceof Map ) |         else if( object instanceof Map ) | ||||||
|         { |         { | ||||||
|             // Table: |             // Table: | ||||||
|   | |||||||
| @@ -41,4 +41,17 @@ public class StringUtil | |||||||
|     { |     { | ||||||
|         return net.minecraft.util.text.translation.I18n.translateToLocalFormatted( key, format ); |         return net.minecraft.util.text.translation.I18n.translateToLocalFormatted( key, format ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static byte[] encodeString( String string ) | ||||||
|  |     { | ||||||
|  |         byte[] chars = new byte[ string.length() ]; | ||||||
|  |  | ||||||
|  |         for( int i = 0; i < chars.length; ++i ) | ||||||
|  |         { | ||||||
|  |             char c = string.charAt( i ); | ||||||
|  |             chars[ i ] = c < 256 ? (byte) c : 63; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return chars; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -639,8 +639,8 @@ end | |||||||
| if http then | if http then | ||||||
|     local nativeHTTPRequest = http.request |     local nativeHTTPRequest = http.request | ||||||
|  |  | ||||||
|     local function wrapRequest( _url, _post, _headers ) |     local function wrapRequest( _url, _post, _headers, _binary ) | ||||||
|         local ok, err = nativeHTTPRequest( _url, _post, _headers ) |         local ok, err = nativeHTTPRequest( _url, _post, _headers, _binary ) | ||||||
|         if ok then |         if ok then | ||||||
|             while true do |             while true do | ||||||
|                 local event, param1, param2, param3 = os.pullEvent() |                 local event, param1, param2, param3 = os.pullEvent() | ||||||
| @@ -654,16 +654,16 @@ if http then | |||||||
|         return nil, err |         return nil, err | ||||||
|     end |     end | ||||||
|      |      | ||||||
|     http.get = function( _url, _headers ) |     http.get = function( _url, _headers, _binary) | ||||||
|         return wrapRequest( _url, nil, _headers ) |         return wrapRequest( _url, nil, _headers, _binary) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     http.post = function( _url, _post, _headers ) |     http.post = function( _url, _post, _headers, _binary) | ||||||
|         return wrapRequest( _url, _post or "", _headers ) |         return wrapRequest( _url, _post or "", _headers, _binary) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     http.request = function( _url, _post, _headers ) |     http.request = function( _url, _post, _headers, _binary ) | ||||||
|         local ok, err = nativeHTTPRequest( _url, _post, _headers ) |         local ok, err = nativeHTTPRequest( _url, _post, _headers, _binary ) | ||||||
|         if not ok then |         if not ok then | ||||||
|             os.queueEvent( "http_failure", _url, err ) |             os.queueEvent( "http_failure", _url, err ) | ||||||
|         end |         end | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Daniel Ratcliffe
					Daniel Ratcliffe